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,所以是实现帧级事务同步的必选方案。