一句话总结
Activity 界面并非在 onResume 生命周期调用后立即显示,而是经历了一场由应用、系统和图形服务共同参与的跨进程协作。
一、核心流程:Activity 界面的诞生
1. ActivityThread 的主线程调度
当 Activity 完成 onCreate 和 onStart 生命周期后,ActivityManagerService (AMS) 通过 Binder 机制向应用进程发送 scheduleResumeActivity() 调用。应用进程的 ActivityThread 接收到该调用后,并不会立即执行,而是将一个 RESUME_ACTIVITY 消息放入主线程的消息队列。这种 Binder IPC 与 Handler 消息机制的解耦,确保了所有界面操作都在主线程上按序执行。最终,主线程的 Handler 调度执行 handleResumeActivity() ,Activity 的 onResume() 生命周期方法才得以调用。
2. WindowManager:为 View 申请一块画布
handleResumeActivity() 的核心任务是使 Activity 的界面可见。它通过 WindowManagerGlobal 的 addView() 方法,将 DecorView(Activity 窗口的根视图)添加到 Window 中。这个过程并非简单的“添加”,而是一个复杂的多进程协作:
- 创建
ViewRootImpl:WindowManagerGlobal会为DecorView创建一个**ViewRootImpl** 实例。ViewRootImpl是一个至关重要的“代理”,它将View树与系统窗口服务连接起来。 - Binder IPC:
ViewRootImpl随即通过 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 宽高?
在 Activity 的 onCreate 阶段,尽管 setContentView() 已经加载了布局,但 DecorView 尚未被添加到 Window 中,这导致了 ViewRootImpl 尚未创建。因此,View 树的**measure/layout 过程尚未启动**,所有 View 的宽高自然都为 0。
为了在 measure/layout 完成后安全地获取宽高,应利用 异步回调 机制:
View.post(Runnable):将代码块放入主线程消息队列的末尾,确保在绘制流程之后执行。ViewTreeObserver.addOnGlobalLayoutListener():监听View树全局布局事件,当布局完成后会触发回调。onWindowFocusChanged(boolean):当Activity首次获得焦点时,所有视图都已完成绘制。
三、四进程的深度协同
Activity 界面的显示,是以下四个核心进程协同的结果:
- 应用进程 (App) :运行着
ActivityThread,是Activity和View存在的实体。 - SystemServer 进程:包含
AMS和WMS,分别负责Activity生命周期调度和窗口管理。 - SurfaceFlinger 进程:一个独立的图形服务,负责最终的窗口合成和显示。
- Zygote 进程:负责孵化应用进程。虽然它不直接参与界面绘制,但它是
Activity能够存在的先决条件。
理解这些进程各自的角色和它们之间的通信(Binder IPC、Socket 等),是理解 Android 架构的关键。