Choreographer
问题
Choreographer 是什么?它如何协调 VSync 信号和 UI 渲染?
答案
Choreographer 的作用
Choreographer(编舞者)是 Android 渲染系统的核心调度器,协调 VSync 信号和 UI 渲染的时机:
回调类型和执行顺序
| 回调类型 | 执行顺序 | 说明 |
|---|---|---|
| CALLBACK_INPUT | 1 | 输入事件处理 |
| CALLBACK_ANIMATION | 2 | 动画计算 |
| CALLBACK_INSETS_ANIMATION | 3 | Insets 动画 |
| CALLBACK_TRAVERSAL | 4 | View 绘制(measure/layout/draw) |
| CALLBACK_COMMIT | 5 | 帧提交后的清理 |
VSync 与帧率
VSync 信号 |----|----|----|----|----|
16ms 16ms 16ms 16ms 16ms (60fps)
理想情况:
VSync → 处理 → 提交帧 → VSync → 处理 → 提交帧
掉帧情况:
VSync → 处理耗时超过 16ms......→ VSync(错过)→ 提交帧
用户看到上一帧重复显示(Jank)
掉帧检测
// 利用 Choreographer 检测掉帧
Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
private var lastFrameTimeNanos = 0L
override fun doFrame(frameTimeNanos: Long) {
if (lastFrameTimeNanos != 0L) {
val durationMs = (frameTimeNanos - lastFrameTimeNanos) / 1_000_000
if (durationMs > 16) {
val droppedFrames = (durationMs / 16) - 1
Log.w("Perf", "Dropped $droppedFrames frames (${durationMs}ms)")
}
}
lastFrameTimeNanos = frameTimeNanos
// 持续监听下一帧
Choreographer.getInstance().postFrameCallback(this)
}
})
与 requestAnimationFrame 的对比
Android 的 Choreographer.postFrameCallback() 类似于浏览器的 requestAnimationFrame():
- 都是在下一帧绘制前触发回调
- 都以屏幕刷新率为基准
- 都用于实现流畅动画
常见面试问题
Q1: Choreographer 和 Handler 的关系?
答案:
Choreographer 底层基于 Handler 消息机制。它通过 native 层注册 VSync 信号监听(FrameDisplayEventReceiver),VSync 到来时向主线程的 Handler 发送一条异步消息(配合同步屏障,保证优先于普通消息处理)。doFrame() 在主线程中执行,按顺序触发各类型回调。
Q2: 为什么自定义动画应该使用 Choreographer.postFrameCallback 而不是 Handler.postDelayed?
答案:
Handler.postDelayed(16ms) 无法精确对齐 VSync 信号,可能在两次 VSync 之间触发,导致帧不稳定。Choreographer.postFrameCallback 保证回调恰好在 VSync 信号到来后执行,与屏幕刷新完全同步,动画更流畅。实际开发中,优先使用 ValueAnimator / ObjectAnimator 等高级 API,它们内部已经使用了 Choreographer。