流畅的界面渲染是用户体验的核心要素之一。屏幕上的每一个像素从数据到最终显示,背后都经历了一场精密的协同作战。本文将解析 Android 渲染系统的核心机制,揭示 VSync 信号、Choreographer、SurfaceFlinger 等关键组件如何协同工作,确保每一帧都能流畅呈现。
整体流程图
从应用层的invalidate、postInvalidateOnAnimation()开始触发刷新,到最终屏幕完成一帧的绘制并展现在屏幕上,核心流程如下所示:
核心角色解析
1. Vsync 垂直信号
Vsync 是一个由硬件产生的定时中断信号,可以理解为总指挥官的 "发令枪",是整个渲染流水线的节奏协调者。
以固定的频率(如60Hz每16.6ms一次)发出信号,强行将CPU、GPU和屏幕刷新这三个原本独立运行的组件的起始时间拉到同一起跑线。确保屏幕只在缓冲区内容完全准备好后才开始读取,避免同时读取和写入同一个缓冲区造成的画面撕裂;
2. Choreographer 编舞者
Choreographer 可以看做 视图的 "指挥家" 。它是连接应用层和系统底层VSync信号的核心枢纽
,负责接收各种绘制请求,并在正确的时机调度执行。通过 postCallback()
方法接收应用层的三种异步更新请求:
CALLBACK_INPUT
:输入事件;CALLBACK_ANIMATION
:动画计算回调,计算并更新View的动画属性(如translationX、alpha等)。这是动画流畅的关键。CALLBACK_TRAVERSAL
:视图树绘制(Measure, Layout, Draw),这是最重量级的步骤,ViewRootImpl
的performTraversals()
会在这里被调用,最终触发整个View树的绘制,生成DrawOp命令列表。
当有回调需要处理时,通过 FrameDisplayEventReceiver
向系统订阅下一次VSync信号。在收到VSync信号后,在 doFrame()
中按照上述固定顺序执行所有收集到的回调。
此外,开发者还可以主动通过 Choreographer 添加回调,在两次回调中通过前后时间差来算出是否产生丢帧现象,如下实现:
kotlin
Choreographer.getInstance().postFrameCallback(object : FrameCallback {
var lastFrameTimeNanos = 0L
var currentFrameTimeNanos = 0L
override fun doFrame(frameTimeNanos: Long) {
//注册的Vsync回调
log("doFrame")
if (lastFrameTimeNanos == 0L) {
lastFrameTimeNanos = frameTimeNanos
}
currentFrameTimeNanos = frameTimeNanos
val diffMs = (currentFrameTimeNanos - lastFrameTimeNanos).nanoseconds.inWholeNanoseconds
if (diffMs > 16.6f) {
val droppedCount = (diffMs / 16.6)
log("发生丢帧")
}
Choreographer.getInstance().postFrameCallback(this);
}
})
上述代码中需要注意,Choreographer 必须主动订阅Vsync信号才能接收。无UI更新时则不订阅就不会收到回调,节省资源。
3. Choreographer.doFrame() 执行时机
doFrame() 是一帧工作的核心 。此时是真正开始构建一帧画面的起点 。具体路径是:VSync -> FrameDisplayEventReceiver.onVsync()
-> 发送异步消息 -> MessageQueue
-> run()
-> doFrame()
。
ini
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
try {
synchronized (mLock) {
//1、掉帧计算
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= frameIntervalNanos) {
final long skippedFrames = jitterNanos / frameIntervalNanos;
final long lastFrameOffset = jitterNanos % frameIntervalNanos;
frameTimeNanos = startNanos - lastFrameOffset;
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
mLastVsyncEventData = vsyncEventData;
}
//2、执行各种回调
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
}
}
上述源码来自API 32,可以看到在doFrame()主要执行了2个操作,1处是进行了掉帧计算 :首先检查执行时间是否超过了帧期限(16.6ms),计算并记录掉帧情况。2处是执行回调:按固定顺序执行队列任务,主要的是INPUT、ANIMATION、TRAVERSAL回调。
4. SurfaceFlinger
SurfaceFlinger 负责最终的合成与提交显示到驱动 。它是一个运行在系统进程中的守护服务。 它可以根据Z-order、透明度、位置等信息,将所有可见图层的最终缓冲区 合成在一起,产生整个屏幕的最终像素数据;此外还会将合成好的帧数据提交给屏幕显示驱动,最终驱动屏幕刷新。
5. 屏幕 Display
Display 负责结果的最终呈现 。它会以固定的物理频率(如60Hz)从Frame Buffer中逐行读取像素数据并点亮屏幕上的像素点;不过Display 的刷新动作同样受VSync信号同步,确保只在SurfaceFlinger完成合成送显后,才从缓冲区读取新数据。

6. RenderThread 渲染线程
RenderThread 是异步GPU命令执行者。一个在应用进程内,但是一个独立的专有线程。负责任务:
- 异步绘制 :接收UI线程录制好的绘制命令(DisplayList),并在后台交由GPU执行,不阻塞UI线程。
- 栅格化:将UI线程传来的矢量绘制命令(如画圆、绘制纹理)转换为最终的像素信息,这是一个极其耗时的过程,由GPU完成。
- 缓冲区管理 :负责管理BufferQueue,将渲染好的帧放入队列,并通知SurfaceFlinger来取。它直接处理了Back Buffer的渲染和交换(swap)操作。
总结:一帧的协作流程
- 应用通过
invalidate
或动画更新等指令发出任务给 Choreographer ,Choreographer 订阅下一次 VSync 信号同步节奏; - 当VSync 信号到达时,
Choreographer.doFrame()
开始执行,在doFrame()
中,按顺序处理动画计算 和视图绘制,在UI线程生成绘制命令。 - UI线程将命令同步给 RenderThread ,由它异步地交给GPU 在Back Buffer 上进行栅格化 。渲染完成后,RenderThread 将缓冲区交换到前台,SurfaceFlinger 获取所有App的最终缓冲区并进行合成。
- 下一个 VSync 信号到来时(注意是下一个Vsync信号),屏幕 从准备好的 Frame Buffer 中读取数据,完成显示。
任何一个环节超时(通常是UI线程的 doFrame
或RenderThread的栅格化),都会导致交换失败,SurfaceFlinger 只能继续使用旧帧数据,用户就看到了掉帧现象。
从 Choreographer 的调度到 SurfaceFlinger 的合成,从 UI 线程的计算到 RenderThread 的渲染这一完整流程,Android 的渲染系统是一个精密而高效的协同体系,每一个环节都经过精心设计,缺一不可。