Android View 绘制与布局:从应用到屏幕的渲染管线

本文目标:打破“只背 View 三部曲”的局限,用一条完整的渲染管线把「应用层代码」到「屏幕显示」的整个链路串联起来。不仅知其然(流程是什么),更知其所以然(为什么要这么设计)。

阅读建议


一、核心结论

不要把「只有 View 三步」当成渲染的全部;这三步仅仅是应用侧准备数据的过程,真正的上屏必须经过合成与显示链路。现代 Android 渲染的宏观分工如下:


二、渲染管线与 VSYNC 机制

屏幕是一行行刷新的,如果代码随时随地去修改像素,极易出现画面撕裂。为此,Android 设计了基于 VSYNC(垂直同步信号) 的节奏控制流水线。

1. 一帧画面的 5 个阶段全貌

sequenceDiagram
    participant Disp as 物理屏幕 & VSYNC
    participant Choreo as 框架层 (Choreographer)
    participant Main as 应用主线程 (UI 线程)
    participant RT as 渲染线程 (RenderThread)
    participant SF as 系统合成进程 (SurfaceFlinger)

    Disp->>Choreo: 1. 发出 VSYNC 节拍 (如每 16.6ms)
    Choreo->>Main: 2. 触发回调,唤醒主线程
    Note over Main: 3. 遍历视图树<br/>(measure -> layout -> draw)
    Main->>RT: 4. 同步绘制指令 (DisplayList)
    Note over RT: 5. GPU 栅格化,生成像素缓冲
    RT->>SF: 6. 跨进程提交缓冲
    Note over SF: 7. 多窗口/图层合成
    SF->>Disp: 8. 送显上屏 (等待下一次 VSYNC)
  1. 节拍器(VSYNC):屏幕按固定频率发出的“滴答”声,统一渲染节奏。
  2. 传达室与发令枪(Choreographer):应用调用 invalidate() 时,是向它“注册”一个任务,等待下一个 VSYNC 发令枪响,才唤醒主线程干活。
  3. 主线程遍历(View 三部曲):主线程确认视图大小(measure)、位置(layout),并录制绘制指令(draw)。
  4. 独立包工头(RenderThread + GPU):主线程录制完图纸后立刻交接给它。它负责真正的“上色”(栅格化),让主线程能解放出来去响应触摸事件。
  5. 幕后大 Boss(SurfaceFlinger):Android 专属的独立系统守护进程。负责把手机所有图层跨进程汇总上屏。

2. 底层设计的演进与解惑


三、主线程遍历详解(measure / layout / draw)

当主线程被 VSYNC 唤醒后,ViewRootImpl.performTraversals() 开始了对视图树的遍历。

1. 视图遍历机制与数据结构模型

从底层数据结构的角度而言,Android 视图系统本质上是一棵以 ViewRootImpl 为根节点的树。视图树的渲染过程(即 performTraversals())严格遵循树的深度优先遍历(DFS)机制。三大核心阶段在遍历模型中的映射如下:

2. 三大阶段细节剖析

阶段一:Measure(测量)

解决“每个 View 想要多大”。父 View 会把尺寸规格打包成 MeasureSpec 向下推送到子节点。

防坑:当父是 AT_MOST(上限)时,子若要求 match_parent,拿到的也是 AT_MOSTmatch_parent 在父未敲定尺寸时,语义退化为「在上限内尽量大」。

阶段二:Layout(布局)

解决“相对父容器放在哪儿”。父容器使用测量出的尺寸,调用 layout(left, top, right, bottom) 定位子 View。

阶段三:Draw(绘制)

解决“怎么画”。主线程的 onDraw 不直接操作 CPU 绘制像素,而是把 Canvas API 录制成 DisplayList,挂载到 RenderNode 上。局部 View 改变时,其他未改变节点的图纸直接重用。


四、常见场景与排障指南

1. 刷新机制:invalidate、requestLayout 与 postInvalidate

它们都不会立刻改变像素,而是向 Choreographer 注册下一个 VSYNC 的请求。

2. 获取尺寸:如何在 onCreate 里安全拿到 View 宽高?

onCreate 中直接调 getWidth() 为 0,因为此时还没等到 VSYNC 发令枪,尚未发生遍历。

3. 卡顿定位:主线程还是 GPU?

4. 自定义 View 开发最佳实践

5. 性能优化:多次测量(Double Taxation)与层级扁平化

6. 线上排障:APM 如何监控 UI 卡顿(Jank)?

了解了 VSYNC 与渲染管线,就能明白业内主流 APM(如 BlockCanary、Matrix)监控卡顿的底层原理:


五、关联参考

知识点引用位置
View 绘制流程详解见 [[Android 面试·分章系统复习#第四章:View 绘制流程(measure → layout → draw)]]
事件分发机制见 [[Android 面试·分章系统复习#第五章:事件分发]]