Skip to content
阅读进度

VSync 一直有,应用为什么不一直执行 doFrame? ​

返回 View 与显示链路 目录

标准回答 ​

VSync 为显示相关工作提供时间节奏;Choreographer 根据待执行的帧回调安排应用的一帧。应用有动画、遍历等工作时,会请求适当的帧信号,再在所属 Looper 线程执行 doFrame。静止页面没有新的帧工作时,不需要为了每次屏幕刷新都重新绘制。

doFrame 主要组织输入相关回调、动画、Insets 动画、遍历和提交等阶段。ViewRootImpl 的遍历任务在相应阶段执行,但没有布局变化时,并不要求每一帧都重新测量和布局。这里的输入阶段也不意味着所有输入事件只能等到 doFrame 才派发。

主线程若正在执行耗时任务,帧信号也不能抢占当前 Java 调用栈。等线程有机会处理时,预期时机可能已经错过。这就是为什么“系统有 VSync”并不能保证应用每帧及时更新。

60Hz 的刷新间隔约 16.7ms,120Hz 约 8.3ms,这是理解节奏的参考。实际调度还涉及帧期限、流水线与刷新率变化,不能把每个渲染阶段都看成各自拥有完整 16.7ms。

关键机制 ​

  • Choreographer 依附于 Looper;常见 View UI 使用主线程上的实例,它不是单独开出的一条绘制线程。
  • 持续动画会继续安排下一帧;postFrameCallback 本身是一次回调,持续循环需要继续注册并正确取消。
  • 屏幕可以重复显示已有内容,屏幕刷新率不等于应用实际产出新帧的频率。

通俗例子 ​

页面静止时主线程可以等待消息,显示器仍显示已有画面;点击按钮启动动画后,才会继续调度动画和相应更新。

追问与回答 ​

用 Handler 每隔 16ms 刷新能替代帧调度吗? ​

不能可靠替代。16ms 是固定延迟,不随真实刷新率和帧时机变化,消息也可能排队,长期还会产生节奏偏差。通常使用系统动画、postOnAnimation 或适当的 Choreographer 接口,让更新配合显示节奏,并管理回调生命周期。

易混淆点 ​

VSync 不会强制中断主线程任务;看到 doFrame 也不代表必然产生新 buffer 或已显示新内容。

参考资料 ​

内容来源 ​

用完整回答理解机制,用真实证据解释项目。