ViewRootImpl 和 Choreographer
关键概念:VSync 信号
- 定义 :显示设备以固定刷新率(如 60Hz/90Hz/120Hz)刷新屏幕时,由
SurfaceFlinger
生成的垂直同步信号。- 作用:确保渲染操作与屏幕刷新同步,避免画面撕裂(Screen Tearing),实现流畅渲染。
ViewRootImpl 概述
ViewRootImpl
是 Android 视图树的根节点,它充当了 Android 视图层次结构(View Hierarchy)与 Android 窗口系统之间的桥梁。
每个 Activity 或 Dialog 都拥有一个 Window
,而每个 Window
又都对应一个 ViewRootImpl
。实际上,ViewRootImpl
就是整个视图树的根,它负责以下关键任务:
-
视图层次结构的起点 :
ViewRootImpl
位于应用程序 UI 视图层次结构的最顶端,它直接与Window
相连,是 Android 系统和应用程序 UI 之间交互的起始点。用户界面上的所有显示内容都是从这个根节点开始进行绘制的。 -
布局、测量和绘制的协调者(Layout,Measure,Draw) :当您请求重新布局视图或需要重绘视图时,实际上是
ViewRootImpl
接收到这些请求并协调整个视图树的布局、测量和绘制过程。它会执行performTraversals()
方法,这个方法是整个渲染管道的核心,它会依序执行:-
performMeasure()
: 测量视图树中每个视图的大小。 -
performLayout()
: 根据测量的大小和布局参数,确定每个视图在屏幕上的位置。 -
performDraw()
: 绘制每个视图的内容到 Surface 上,最终显示在屏幕上。
-
-
事件分发 (Event Dispatch) :
ViewRootImpl
也负责接收来自系统的输入事件 (例如触摸事件、按键事件等),并将这些事件分发到视图树中相应的视图进行处理。 -
Surface 管理 :
ViewRootImpl
负责管理与窗口相关联的Surface
。Surface
是用于绘制图形的接口,您可以将它想象成屏幕上的一块画布。ViewRootImpl
负责获取、配置和锁定Surface
,以便视图可以将其绘制内容渲染到Surface
上。
Choreographer 概述
Choreographer 是 Android 图形渲染系统中至关重要的核心组件,负责协调动画、布局和绘制等操作,使其与显示设备的垂直同步信号(VSync)保持同步,进而实现流畅的用户界面渲染。
它的主要职责包括:
- 帧同步(Frame Synchronization) :
Choreographer
与屏幕的刷新率同步。大多数 Android 设备的屏幕刷新率为 60Hz (每秒刷新 60 次),这意味着系统每 16.6 毫秒 (1000ms / 60Hz) 刷新一次屏幕。Choreographer
确保您的应用程序的绘制操作与这个刷新率同步,以避免画面撕裂 (screen tearing) 和确保流畅的动画。 - 安排回调(Callback Scheduling) :
Choreographer
会安排不同类型的回调,在每个垂直同步信号 (VSync signal) 到达时执行,这些回调类型包括:- 输入回调(Input Callback):处理输入事件。
- 动画回调(Animation Callback):执行动画的更新。
- 绘制回调(Draw Callback):执行视图的绘制操作。
- 延迟动画回调((Deferred Animation Callback):用于在动画和绘制之间插入额外的动画步骤。
- VSync 信号监听 :
Choreographer
会监听来自底层显示系统的垂直同步信号 (VSync signal)。当 VSync 信号到达时,Choreographer
就会知道新的一帧开始了,并开始执行安排好的回调。 - 保证流畅的 UI 渲染 : 通过与 VSync 信号同步并合理安排各类型的回调,
Choreographer
帮助确保 UI 渲染的流畅性,达到 60fps 或更高的帧率,从而提供良好的用户体验。
Choreographer
是 Android 渲染系统的心跳,它协调和同步所有渲染相关的操作,确保 UI 渲染的流畅和高效。
ViewRootImpl 渲染过程
在上一篇文章中,请求绘制指令都来到了 ViewRootImpl 的 scheduleTraversals 函数中:
java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 发送一个同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
ViewRootImpl 在 scheduleTraversals()
里先发送了一个同步屏障消息,然后调用 Choreographer
的 postCallback
函数,发送了一个 mTraversalRunnable
:
java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
这里可以看出这个 TraversalRunnable 真正执行的是 ViewRootImpl 的 doTraversal()
:
java
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper()
.getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
同步消息屏障的作用
在
scheduleTraversals()
里先发送了一个同步屏障消息,然后在doTraversal()
中,移除了这个消息屏障,这个机制是为了确保 View 的布局、测量和绘制过程能够在一个相对 uninterrupted 的环境中进行,从而提高 UI 渲染的性能和流畅性。这里发送同步屏障的是 mHandler,它的类型是 ViewRootHandler,它用来处理一些 UI 相关的消息,比如执行 invalidate,屏幕移除,处理输入事件等。
在
scheduleTraversals()
中,给编舞者发了一个 CALLBACK_TRAVERSAL 类型的 Callback:
javamChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
也就是说同步屏障在这里保证的就是这个 Callback 对应的异步消息能够尽快处理。
同步屏障在
performTraversals()
执行之前被移除。 这似乎有些反直觉,但实际上,同步屏障的主要目的是 在CALLBACK_TRAVERSAL
消息被处理之前生效,并在 Traversal 过程开始之前被移除。同步屏障的重点在于创建一个更干净的消息处理环境,以便 performTraversals 能够 更快速地开始执行,而不是让 performTraversals 本身在屏障期间被优先执行。
真正的处理渲染过程
在 doTraversal()
中,移除了消息屏障,然后执行 performTraversals()
,performTraversals()
是 ViewRootImpl 的核心方法,performTraversals()
主要处理三个核心步骤:measure(测量)、layout(布局)、draw(绘制):
1. 测量(Measure)
关键代码:
java
// 测量视图层级
windowSizeMayChange |= measureHierarchy(host, lp, resources, desiredWidth, desiredHeight, optimize);
// 执行具体测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
核心逻辑:
- 通过
measureHierarchy
递归测量所有子视图的尺寸。 - 根据窗口参数(如
WRAP_CONTENT
)动态调整测量规格。 - 权重(Weight)处理:若布局参数包含权重,可能触发多次测量。
2. 布局(Layout)
关键代码:
java
// 执行布局
performLayout(lp, mWidth, mHeight);
// 处理透明区域
if ((host.mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
host.gatherTransparentRegion(mTransparentRegion);
mTransaction.setTransparentRegionHint(sc, mTransparentRegion);
}
核心逻辑:
- 调用
performLayout
确定所有子视图的位置。 - 收集透明区域信息,通过
SurfaceControl.Transaction
告知系统优化合成。
3. 绘制(Draw)
关键代码:
java
// 触发绘制
if (!cancelAndRedraw) {
performDraw();
}
// 硬件加速初始化
if (surfaceCreated && mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.initialize(mSurface);
mAttachInfo.mThreadedRenderer.allocateBuffers();
}
核心逻辑:
- 调用
performDraw
生成绘制命令,通过Canvas
或ThreadedRenderer
渲染内容。 - 若启用硬件加速,初始化渲染器并将 Surface 绑定到 GPU。
4. Surface 管理
关键代码:
java
// 与 WindowManagerService 通信更新 Surface
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
// Surface 状态处理
if (surfaceCreated) {
mFullRedrawNeeded = true; // Surface 新建需完全重绘
} else if (surfaceDestroyed) {
destroyHardwareResources(); // Surface 销毁释放资源
}
核心逻辑:
- 通过
relayoutWindow
更新窗口布局并获取新的 Surface。 - 根据 Surface 状态(新建/销毁/替换)触发渲染器初始化或资源释放。
5. 合成与提交
关键代码:
java
// 提交绘制结果
if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
drawSoftware(surface, mAttachInfo, xOffset, yOffset);
}
核心逻辑:
- 通过
ThreadedRenderer.draw
(硬件加速)或drawSoftware
(软件绘制)提交帧数据。 - 最终通过 SurfaceFlinger 合成到屏幕。
核心流程总结
- 测量:递归计算所有视图的尺寸。
- 布局:确定所有视图的位置。
- Surface 准备:通过 WMS 获取/更新 Surface。
- 绘制:生成像素数据并提交到 Surface。
- 合成:由系统将 Surface 内容显示到屏幕。
Choreographer 协调渲染过程
postCallback
ViewRootImpl 中通过 mChoreographer 的 postCallback 函数发送了一个 TraversalRunnable 来执行绘制流程,下面进入 Choreographer 的部分,首先从 postCallback 开始:
java
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
postCallback 内部本质是调用 postCallbackDelayedInternal
:
java
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
首先加锁确保同步,然后根据开机时间计算出合理的执行时间,即当前系统启动以来的毫秒数 ,然后将这个 callback 添加到 mCallbackQueues,这是一个回调队列数组 ,Choreographer
使用它来管理不同类型的回调。数组的索引就是 callbackType
,每个元素 mCallbackQueues[callbackType]
都是一个 CallbackQueue
对象,负责管理特定类型的所有待执行回调。
addCallbackLocked(dueTime, action, token);
方法负责将新的回调任务添加到指定类型 (callbackType
) 的回调队列 中,队列中按照 dueTime
排序 (通常是按到期时间升序排列)。
if (dueTime <= now)
: 判断回调的到期时间是否已经到达或已经过期 。如果 delayMillis
为 0 或负数,或者由于系统时钟偏差等原因导致 dueTime
小于等于 now
,则表示回调应该立即执行 (或者尽快在下一个 VSync 信号到达时执行)。
scheduleFrameLocked(now);
: 立即调度一个帧 (Frame) 。 scheduleFrameLocked
方法 (也是 Choreographer
类的方法,并且线程安全) 的作用是请求在下一个 VSync 信号到达时执行帧处理。这会触发 Choreographer
的核心渲染流程,在 VSync 信号到达时,会检查并执行所有到期的回调队列中的回调任务 (包括刚刚添加的这个)。
否则,发送 Handler 异步消息,在未来延迟执行。
scheduleFrameLocked 请求下一帧渲染
进入 scheduleFrameLocked , 看看编舞者是如何调度一个帧的:
java
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
// 如果正在 Looper 线程上运行,那么立即安排垂直同步(vsync);
// 否则,发布一条消息以便尽快从用户界面(UI)线程安排垂直同步。
if (Looper.myLooper() == mLooper) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
scheduleFrameLocked 中,如果不是立即执行的消息,通过 FrameHandler (mHandler)发送了一个
MSG_DO_FRAME 的异步消息到主线程。
而立即执行的消息,帧调度核心逻辑在 scheduleVsyncLocked 方法:
java
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
mDisplayEventReceiver 的类型是 FrameDisplayEventReceiver
,它是 Choreographer
里面的一个核心组件 ,它的主要职责是接收来自系统底层的 VSync 信号 ,并将这些信号传递给 Choreographer
进行处理,从而驱动整个渲染流程。 你可以把它想象成是 Choreographer
的 VSync 信号接收器和处理者。
DisplayEventReceiver
java
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
private final VsyncEventData mLastVsyncEventData = new VsyncEventData();
FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
super(looper, vsyncSource, 0, layerHandle);
}
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
try {
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (!mHavePendingVsync) {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
mLastVsyncEventData.copyFrom(vsyncEventData);
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
// 这里的 mHandler 是 FrameHandler 类型
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}
这里把 VSync 事件作为一个消息 (Message) 投递到 Handler 去处理,而不是直接在 onVsync()
回调方法里立即处理。目的是通过 Handler 消息机制来平衡 VSync 事件处理的及时性和消息队列的公平性。
- 不直接在
onVsync()
中处理: 避免 VSync 事件处理"饿死"消息队列,保证 Handler 线程能够公平地处理各种类型的任务,维持应用的整体响应性。 - 将 VSync 事件投递到 Handler 消息队列: 将 VSync 事件的处理纳入到 Handler 的消息调度体系中,与其他类型的消息统一管理。
- 仍然可以 "立即" 处理 (相对于队列顺序): 在消息队列空闲或者没有更早消息的情况下,VSync 事件消息仍然可以很快得到处理,保证了帧绘制的及时性。
- 保证更早消息的优先处理: 如果消息队列中有更早的消息,则优先处理更早的消息,体现了消息队列的 FIFO 原则,保证了消息处理的顺序性和公平性。
总而言之,这种设计是一种折衷和权衡 ,在 帧绘制的及时性 和 消息队列的公平性 之间找到一个平衡点,既要保证 UI 渲染能够及时响应 VSync 信号,又要避免 VSync 事件处理 "霸占" 线程资源,影响其他重要任务的执行,最终目的是为了提升 Android 系统的整体性能和用户体验。
当 Choreographer
收到了 VSync 信号后,通过 FrameHandler 发送了一个消息。
而上面的 DisplayEventReceiver 的 scheduleVsync 方法:
java
public void scheduleVsync() {
if (mReceiverPtr == 0) {
// log something
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
从这里进入 native 层,也就是说 Vsync 信号来自于底层。
FrameHandler
可以看出,每一帧的时机到来,都通过 Handler 机制,将消息发送到主线程,在主线程执行帧绘制操作。
FrameHandler
是 Choreographer
类中的一个私有内部类 ,它继承自 Handler
。FrameHandler
在这里专门用来处理 Choreographer
内部的消息,驱动着 Choreographer
的核心运作。
java
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
doFrame
触发一帧的绘制操作 ,doFrame()
方法是 Choreographer
最核心的方法之一,它负责执行实际的帧绘制工作,包括执行所有类型的回调(输入、动画、绘制、延迟动画),完成一帧画面的渲染。
java
void doFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) {
synchronized (mLock) { // 线程同步锁
if (!mFrameScheduled) { // 检查是否需要调度帧
return;
}
// ... (省略:时间抖动校正、跳帧检测、帧时间倒退/FPS限制检查 等细节) ...
// ... (省略:记录 VSync 信息、更新状态 等细节) ...
} // 结束 synchronized 区块
// 锁定动画时钟 (同步动画时间)
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
// 执行各阶段的回调 (核心渲染阶段)
doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos); // 输入回调
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos); // 动画回调
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos);// Insets 动画回调
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos); // Traversal 回调 (布局/测量)
doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos); // Commit 回调 (提交)
// 解锁动画时钟
AnimationUtils.unlockAnimationClock();
}
核心渲染阶段在 doCallbacks:
java
void doCallbacks(int callbackType, long frameIntervalNanos) {
CallbackRecord callbacks;
long frameTimeNanos = mFrameData.mFrameTimeNanos;
synchronized (mLock) {
// 获取到期的回调列表
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return; // 如果没有到期的回调,直接返回
}
mCallbacksRunning = true; // 标记回调开始运行
// ... (省略: Commit 回调的 Jitter 补偿和帧时间调整 的细节) ...
}
try {
// 遍历并执行回调函数
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(mFrameData); // 执行回调函数的核心方法
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false; // 标记回调运行结束
// 回收 CallbackRecord 对象 (为了复用)
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
}
}
这里就是从 mCallbackQueues 取出不同类型消息的回调并执行,在 postCallback 中,有向 mCallbackQueues 添加回调的逻辑,也就是会执行到 ViewRootImpl 的 doFrame 函数中执行 View 的真正绘制流程。
doScheduleVsync
请求调度 VSync 信号, 向系统底层请求 VSync 信号,以便在下一个 VSync 信号到来时开始新的帧绘制。
java
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
最终会调用到 mDisplayEventReceiver 的 scheduleVsync 函数:
java
public void scheduleVsync() {
if (mReceiverPtr == 0) {
// log something
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
调用 native 方法请求 VSync。
doScheduleCallback
检查并调度帧,以便及时执行特定类型的延期回调 (Delayed Callback) ,根据 callbackType
参数,从对应的回调队列中取出所有到期的回调函数,并执行它们。
java
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
// 检查是否存在需要立即执行的 callback
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
直接去从 mCallbackQueues 找是否存在到期的 Delay Callback 需要执行,从而能够尽快安排下一帧的渲染流程,从而触发这些回调的执行。
小结
- 消息接收和分发中心:
FrameHandler
是Choreographer
内部的消息处理中心,它接收来自Choreographer
线程消息队列的消息,并根据消息类型进行分发处理。 - 驱动帧渲染流程: 通过处理
MSG_DO_FRAME
消息,FrameHandler
触发doFrame()
方法,启动一帧的完整渲染流程,包括执行各种回调和绘制操作。 - 调度 VSync 信号: 通过处理
MSG_DO_SCHEDULE_VSYNC
消息,FrameHandler
触发doScheduleVsync()
方法,请求系统调度 VSync 信号,实现与屏幕刷新的同步。 - 执行延迟回调: 通过处理
MSG_DO_SCHEDULE_CALLBACK
消息,FrameHandler
触发doScheduleCallback()
方法,执行各种类型的延迟回调,包括输入事件处理、动画更新和绘制等。
总结
scss
应用程序 UI 代码 (View.invalidate())
↓
View.invalidate() -> mParent.invalidateChild(...) (请求向上冒泡)
↓
... (invalidate 请求在 View 树中向上冒泡) ...
↓
DecorView.invalidate() -> ViewRootImpl.invalidateChildInParent(...)
↓
ViewRootImpl.invalidateChildInParent() -> ViewRootImpl.scheduleTraversals() (请求 Traversal 调度)
↓
ViewRootImpl.scheduleTraversals() -> Choreographer.postCallback(CALLBACK_TRAVERSAL, action, null)
↓
Choreographer.postCallback() 检查 CALLBACK_TRAVERSAL 队列是否为空,如果为空,则调用 Choreographer.scheduleFrameLocked()
↓
Choreographer.scheduleFrameLocked() -> mDisplayEventReceiver.scheduleVsync() (请求 VSync 信号)
↓
SurfaceFlinger 生成 VSync 信号,通过 DisplayEventReceiver 的 onVSync 回调给 Choreographer
↓
DisplayEventReceiver -> Choreographer.FrameHandler (发送 MSG_DO_FRAME 消息)
↓
Choreographer.FrameHandler -> Choreographer.doFrame(frameTimeNanos, vsyncSeq) (启动帧渲染流程)
↓
Choreographer.doFrame() -> 执行 CALLBACK_TRAVERSAL 队列中的任务 (mTraversalRunnable -> ViewRootImpl.performTraversals())
↓
ViewRootImpl.performTraversals() -> View 树 Measure, Layout, Draw
↓
最终渲染结果显示在屏幕上
Choreographer
基于 VSync 信号,通过 doFrame()
方法驱动渲染流程。 Choreographer
是渲染流程的 调度器 和 指挥家。
SurfaceFlinger
发送的 VSync 信号是最底层的触发器 , 它驱动了 Choreographer
的 doFrame()
方法,进而驱动了整个 Android 图形渲染管道。
在请求渲染之后,通过 Choreographer 接管渲染调度,来安排渲染的执行,最终交给 ViewRootImpl 去执行渲染绘制。