当 Activity.onResume() 执行完毕,你以为界面就出现了?不,那只是逻辑上的“就绪”。
真正的魔法发生在接下来的 16.67 毫秒(即 1/60 秒)内。
在这极短的瞬间,CPU 必须完成测量、布局、绘制指令生成;GPU 必须完成光栅化;合成器必须将图层叠加并输出到屏幕。如果任何一步超时,用户就会看到卡顿(Jank)。
本篇作为本系列的终章,我们将深入 Android 图形系统的核心,揭秘 Choreographer、ViewRootImpl 和 SurfaceFlinger 如何协同工作,创造出流畅的视觉体验。
第一步:时间的指挥家 —— Choreographer
Android 流畅度的秘密武器是 Choreographer(编舞者) 。它不与业务逻辑纠缠,只专注一件事:同步 VSync 信号。
什么是 VSync?
显示屏以固定频率(如 60Hz)刷新。每次刷新前,硬件会发出一个垂直同步信号(VSync)。Choreographer 监听这个信号,确保所有的绘制操作都在新的刷新周期开始时启动,避免画面撕裂。
代码位置:frameworks/base/core/java/android/view/Choreographer.java
// Choreographer 的核心循环逻辑
private void doFrame(long frameTimeNanos, int frame) {
// 1. 计算是否掉帧 (如果当前时间比预期晚了很多)
long intendedVsync = mVsyncTimeOffset + frame * mFrameIntervalNanos;
if (frameTimeNanos > intendedVsync + mFrameIntervalNanos) {
Log.w("Choreographer", "Skipped frames!"); // 掉帧警告
}
// 2. 【关键】按顺序执行四个阶段的回调
// 顺序严格保证:输入 -> 动画 -> 布局/绘制 -> 提交
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); // 处理触摸事件
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); // 执行属性动画
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); // 执行 measure, layout, draw
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); // 提交绘制任务给 GPU
// 3. 请求下一帧
scheduleVsync();
}
核心机制:所有 UI 更新(
invalidate(),requestLayout())本质上都是向 Choreographer 注册一个CALLBACK_TRAVERSAL任务,等待下一个 VSync 到来时统一执行。这避免了频繁的随机绘制。
第二步:视图树的遍历 —— Measure, Layout, Draw
当 Choreographer 触发 CALLBACK_TRAVERSAL 后,ViewRootImpl 开始驱动整个 View 树进行“体检”和“化妆”。
代码位置:frameworks/base/core/java/android/view/ViewRootImpl.java
流程伪代码:
// ViewRootImpl.performTraversals()
void performTraversals() {
// 1. 测量 (Measure)
// 自顶向下传递约束,自底向上返回尺寸
// 问孩子:“你想要多大?”孩子:“我最多想要 100px。”
mView.measure(measureWidth, measureHeight);
// 2. 布局 (Layout)
// 告诉孩子:“你坐在这里 (x, y),大小是 (w, h)”
mView.layout(l, t, r, b);
// 3. 绘制 (Draw)
// 获取 Canvas,开始录制绘制指令
Canvas canvas = mSurface.lockCanvas();
try {
mView.draw(canvas);
} finally {
mSurface.unlockCanvasAndPost(canvas); // 提交缓冲区
}
}
硬件视角:
- CPU 密集型:Measure 和 Layout 主要是 CPU 计算(递归遍历树,计算几何)。
- 指令录制:
draw()方法并没有直接画像素,而是将“画圆”、“填色”、“贴图”等指令录制到 DisplayList (RenderNode) 中。这是为了后续让 GPU 高效执行。
第三步:GPU 的介入 —— RenderThread 与 HWUI
在现代 Android 版本中,绘制工作不再完全由主线程承担,而是移交给了专门的 RenderThread。
架构变化:
- 主线程 (UI Thread) :负责计算 View 树结构,生成 DisplayList 指令集。
- 渲染线程 (RenderThread) :接收指令,调用 OpenGL ES 或 Vulkan API,将矢量指令转换为纹理(光栅化)。
- GPU:执行着色器程序,输出像素数据到 Framebuffer。
流程图:渲染管线
性能关键:如果主线程在 Measure/Layout 阶段耗时超过 8ms,留给 RenderThread 的时间就不够了,导致无法在下一个 VSync 前完成绘制,产生掉帧。
🔹 第四步:最终的合成 —— SurfaceFlinger
App 绘制好了自己的窗口,但屏幕上还有状态栏、导航栏、其他 App 的悬浮窗。谁来把它们拼在一起?
答案是 SurfaceFlinger(运行在独立进程中的系统服务)。
工作流程:
-
收集缓冲区:SurfaceFlinger 监听所有应用进程的 Graphic Buffer 队列。
-
合成 (Composition) :
- 如果是简单的叠加,GPU 直接合成。
- 如果需要特效(如模糊、转场),可能使用 Hardware Composer (HWC) 硬件层。
-
输出:将最终合成的图像写入 Framebuffer,屏幕控制器读取并显示。
代码位置:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp (C++)
// SurfaceFlinger 的主循环简化逻辑
void SurfaceFlinger::mainLoop() {
while (true) {
// 1. 等待 VSync 或新缓冲区到达
waitForEvent();
// 2. 锁定当前状态
LockGuard lock(mStateLock);
// 3. 遍历所有图层 (Layers)
for (auto& layer : mLayers) {
if (layer->hasNewBuffer()) {
// 获取最新的 GraphicBuffer
sp<GraphicBuffer> buf = layer->getNextBuffer();
composeLayer(buf);
}
}
// 4. 提交给 HWC (Hardware Composer) 或直接绘制
mHwc->commit();
}
}
系列终章总结:从硅片到像素的史诗
至此,我们的《Android 系统启动全景图》系列正式完结。让我们最后一次回顾这段从硬件复位到像素显示的壮丽旅程:
| 篇章 | 阶段 | 核心组件 | 关键成就 |
|---|---|---|---|
| 第一篇 | 硬件与Init 进程 | init, ueventd | 挂载文件系统,启动守护进程,解析 init.rc |
| 第二篇 | Zygote 孵化 | app_process, Zygote | 预加载资源,Fork 机制 (COW),创建 Java 世界 |
| 第三篇 | SystemServer | AMS, PMS, WMS | 启动核心服务,注册 Binder,系统就绪 |
| 第四篇 | App 启动 | Launcher, ActivityThread | 进程创建,Binder 通信,生命周期回调 |
| 第五篇 | UI 渲染 | Choreographer, GPU, SurfaceFlinger | VSync 同步,渲染管线,图形合成,画面显示 |
全链路延迟分析:
当你点击图标到看到界面,整个过程经历了:
- Input: 触摸屏中断 -> InputReader -> InputDispatcher (约 2-5ms)
- Logic: Launcher -> AMS -> Zygote -> App Process (约 100-500ms,取决于冷/热启动)
- Render: Measure/Layout/Draw -> GPU -> SurfaceFlinger (约 16ms/帧)
给开发者的启示:
- 启动优化:减少 Application 和 Activity
onCreate中的耗时操作,利用异步。 - 流畅度优化:避免在主线程进行 IO 或复杂计算,确保在 16ms 内完成 Traversal。
- 内存优化:理解 Zygote 的 COW 机制,避免过早修改共享内存导致内存膨胀。
结语
Android 系统是一个宏伟的工程奇迹。它融合了 Linux 内核的稳健、Java 虚拟机的灵活、Binder 机制的高效以及图形管线的精湛。
希望这个系列能帮你建立起对 Android 系统的全局观。下次当你看到手机开机动画,或者流畅地滑动列表时,希望你能在脑海中浮现出这些进程、线程和信号交织共舞的画面。
技术之路无止境,探索永不止步。