registerRtFrameCallback 详细拆解
它是什么
UI线程 RenderThread SurfaceFlinger
│ │ │
View.invalidate() │ │
measure/layout/draw │ │
│ │ │
│──── 同步渲染指令 ────→ │ │
│ 执行GPU绘制命令 │
│ 生成帧缓冲 │
│ │ │
│ ★ registerRtFrameCallback 在这里触发 ★ │
│ │ │
│ │──── 提交帧事务 ────→ │
│ │ 合成显示

关键点:回调触发时,View 树的绘制已经完成(帧缓冲已生成),但帧事务还没提交给 SF。这是合并外部 Transaction 的最佳时机。
回调参数 frame: Long
it?.registerRtFrameCallback { frame: Long →
frame 是帧号(frame number),标识当前绘制的是第几帧。传给 mergeWithNextTransaction(transaction, frame) 时,Framework 用它来确保事务合并到正确的那一帧,而不是错配到后续帧。
回调运行在哪个线程
RenderThread——不是主线程,不是 ScreenFade 线程。这也解释了代码中的一些设计:
it?.registerRtFrameCallback { frame: Long ->
// ← 这里运行在 RenderThread
mMergeWithNextTransaction = true // @Volatile,跨线程可见
if (show) {
if (coverBlackScreenFade()) {
it.mergeWithNextTransaction(mScreenFadeTransaction, frame)
mMergeWithNextTransaction = false
// 需要切回主线程才能触发动画
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
updateScreenFadeState(true)
}
}
}
}
updateScreenFadeState 需要通过 postAtFrontOfQueue 切回主线程,因为 RenderThread 上不能直接操作 Handler/动画。用 postAtFrontOfQueue(而非普通 post)是为了插队到消息队列最前面,尽快启动动画。
回调的一次性特征
registerRtFrameCallback 是一次性的——只在下一帧绘制完成时触发一次,不会持续回调。这正好匹配这里的需求:只需要在 AOD 消失/出现的那一帧同步一次。
完整时序(以 AOD 消失场景为例)
① onAodViewChanged(visible=false)
└── mergeWithNextTransaction(show=true)
└── registerRtFrameCallback(callback) ← 注册,等下一帧
② ViewRootImpl 发起下一帧绘制
├── UI线程: AOD View 设为 GONE → measure/layout/draw
└── RenderThread: 执行 GPU 绘制
③ RenderThread 绘制完成,触发 callback
├── mMergeWithNextTransaction = true ← 标记不要单独 apply
├── coverBlackScreenFade() ← 准备压黑事务(alpha=0.99)
│ └── mScreenFadeTransaction.show(surface)
│ └── 检查 mMergeWithNextTransaction=true → 跳过 applyAsyncUnsafe()
├── it.mergeWithNextTransaction( ← 把压黑事务合并到当前帧
│ mScreenFadeTransaction, frame)
├── mMergeWithNextTransaction = false
└── postAtFrontOfQueue { updateScreenFadeState(true) } ← 启动亮屏动画
④ RenderThread 提交帧事务给 SurfaceFlinger
└── 一个原子事务包含:
├── AOD View 消失的绘制结果
└── 压黑 Layer show + alpha=0.99
→ SF 在同一个 Vsync 周期合成 → 用户看不到中间状态,无闪烁
⑤ 主线程执行 updateScreenFadeState(true)
└── ScreenFade线程: SpringAnimation 从黑渐变到透明
为什么不用 View.post() 或 Choreographer

registerRtFrameCallback 是唯一能精确卡在”绘制完成、提交之前”这个窗口的 API,所以是实现帧级事务同步的必选方案。