Android 界面渲染:从 `onResume` 到像素上屏的深度解构

300 阅读3分钟

一句话总结

Activity 界面并非在 onResume 生命周期调用后立即显示,而是经历了一场由应用、系统和图形服务共同参与的跨进程协作。

一、核心流程:Activity 界面的诞生

1. ActivityThread 的主线程调度

Activity 完成 onCreateonStart 生命周期后,ActivityManagerService (AMS) 通过 Binder 机制向应用进程发送 scheduleResumeActivity() 调用。应用进程的 ActivityThread 接收到该调用后,并不会立即执行,而是将一个 RESUME_ACTIVITY 消息放入主线程的消息队列。这种 Binder IPC 与 Handler 消息机制的解耦,确保了所有界面操作都在主线程上按序执行。最终,主线程的 Handler 调度执行 handleResumeActivity()ActivityonResume() 生命周期方法才得以调用。

2. WindowManager:为 View 申请一块画布

handleResumeActivity() 的核心任务是使 Activity 的界面可见。它通过 WindowManagerGlobaladdView() 方法,将 DecorViewActivity 窗口的根视图)添加到 Window 中。这个过程并非简单的“添加”,而是一个复杂的多进程协作:

  • 创建 ViewRootImplWindowManagerGlobal 会为 DecorView 创建一个**ViewRootImpl** 实例。ViewRootImpl 是一个至关重要的“代理”,它将 View 树与系统窗口服务连接起来。
  • Binder IPCViewRootImpl 随即通过 Binder IPC 向 WindowManagerService (WMS) 请求创建并配置一个窗口
  • 分配 Surface:WMS 在 system_server 进程中为这个新窗口分配一个专用的 Surface(可以理解为一块用于绘制的像素画布),并将其句柄返回给 ViewRootImpl

3. View 树的绘制与 SurfaceFlinger 的合成

ViewRootImpl 获得 Surface 后,便拥有了驱动绘制的权限。它会在一个合适的时机(通常在获得 Surface 后)触发 View 树的首次绘制

  • performTraversals() :这是绘制流程的入口。它按序执行 onMeasure()(测量尺寸)、onLayout()(确定位置)和 onDraw()(将内容绘制到 Surface)。
  • SurfaceFlinger 合成View 绘制完毕后,ViewRootImpl 会将 Surface 的内容提交给 SurfaceFlinger 进程。SurfaceFlinger 是一个独立的图形服务,它负责将所有可见窗口的 Surface 内容进行混合、合成,并最终提交给硬件显示器,使界面呈现在屏幕上。

二、关键时机:为何 onCreate 无法获取 View 宽高?

ActivityonCreate 阶段,尽管 setContentView() 已经加载了布局,但 DecorView 尚未被添加到 Window 中,这导致了 ViewRootImpl 尚未创建。因此,View 树的**measure/layout 过程尚未启动**,所有 View 的宽高自然都为 0。

为了在 measure/layout 完成后安全地获取宽高,应利用 异步回调 机制:

  • View.post(Runnable) :将代码块放入主线程消息队列的末尾,确保在绘制流程之后执行。
  • ViewTreeObserver.addOnGlobalLayoutListener() :监听 View 树全局布局事件,当布局完成后会触发回调。
  • onWindowFocusChanged(boolean) :当 Activity 首次获得焦点时,所有视图都已完成绘制。

三、四进程的深度协同

Activity 界面的显示,是以下四个核心进程协同的结果:

  • 应用进程 (App) :运行着 ActivityThread,是 ActivityView 存在的实体。
  • SystemServer 进程:包含 AMSWMS,分别负责 Activity 生命周期调度和窗口管理。
  • SurfaceFlinger 进程:一个独立的图形服务,负责最终的窗口合成和显示。
  • Zygote 进程:负责孵化应用进程。虽然它不直接参与界面绘制,但它是 Activity 能够存在的先决条件。

理解这些进程各自的角色和它们之间的通信(Binder IPC、Socket 等),是理解 Android 架构的关键。