源码基于 sdk-30/11.0/R。
前言
- Android 平台提供两种信号,一种是硬件信号,另一种是软件信号,由 SurfaceFlinger 进程的一个线程定时发出,硬件信号由硬件发出;
- App 进程若要通过 gpu 实现图像绘制,需要在接收到 Vsync 信号的条件下进行。因此,App 进程访问 SurfaceFlinger 进程获取这个信号,再进行 gpu 绘制;
- Android4.1 之后增加了 Choreographer 机制,用于同 Vsync 机制配合,统一动画、输入和绘制时机;
- Choreographer 就是负责获取 Vsync 同步信号并控制 App 线程(主线程)完成图像绘制的类;
Choreographer 类介绍
实例初始化
/**
Coordinates the timing of animations, input and drawing.
The choreographer receives timing pulses (such as vertical synchronization) from the display subsystem then schedules work to occur as part of rendering the next display frame.
*/
public final class Choreographer {
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
public static Choreographer getInstance() {
return sThreadInstance.get();
}
}
- 每个线程中保存一个 Choreographer 实例对象;
- 线程初始化 Choreographer 对象时需要提供 Looper 对象,并把 Looper 绑定到 Choreographer;
- App 中所有的 Activity 共享同一个 Choregrapher 对象,他控制者整个App中大部分视图的绘制节奏;
构造方法
public final class Choreographer {
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//使用数组保存五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
}
- 根据当前线程的 looper 创建 Handler 对象;
- 变量 USE_VSYNC 用于表示系统是否是用了 Vsync 同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。4.1 以上默认是 true;
- 创建一个 FrameDisplayEventReceiver 对象用于请求并接收 Vsync 事件;
- 也就是数组中有五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
- 创建了一个大小为 5 的 CallbackQueue 队列数组,用于保存不同类型的 Callback;
Callback 类型
// Choreographer.java
//输入事件,首先执行
public static final int CALLBACK_INPUT = 0;
//动画,第二执行
public static final int CALLBACK_ANIMATION = 1;
//插入更新的动画,第三执行
public static final int CALLBACK_INSETS_ANIMATION = 2;
//绘制 View,第四执行
public static final int CALLBACK_TRAVERSAL = 3;
//提交,最后执行。遍历完成的提交操作,用来修正动画启动时间
public static final int CALLBACK_COMMIT = 4;
- 五种类型任务对应存入对应的 CallbackQueue 中;
- 每当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的任务,然后是 ANIMATION 类型,最后才是 TRAVERSAL 类型;
FrameHandler 处理的消息
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);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
FrameHandler 处理了如下三种消息:
- MSG_DO_FRAME 处理注册在 Choreographer 的 Runnable;
- MSG_DO_SCHEDULE_VSYNC 直接请求下一帧的 VSync 信号;
- MSG_DO_SCHEDULE_CALLBACK 处理延时消息;
Choreographer 处理任务流程
public abstract class DisplayEventReceiver {
public void scheduleVsync() {
nativeScheduleVsync(mReceiverPtr);
}
// Called from native code.
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}
// Called when a vertical sync pulse is received.
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
}
}
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
// timestampNanos其实是本次vsync产生的时间,从服务端发过来
// 该消息的callback为当前对象
Message msg = Message.obtain(mHandler, this);
// 前面添加了同步屏障,同步消息不会被执行
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
doFrame(mTimestampNanos, mFrame);
}
void doFrame(long frameTimeNanos, int frame) {
if (!mFrameScheduled) {
return; // no work to do
}
if (frameTimeNanos < mLastFrameTimeNanos) {
// 这种情况一般是生成vsync的机制出现了问题,那就再申请一次
scheduleVsyncLocked();
return;
}
// intendedFrameTimeNanos是本来要绘制的时间戳,frameTimeNanos是真正的,可以在渲染工具中标识延迟VSYNC多少
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
// 移除mFrameScheduled判断,说明处理开始了
mFrameScheduled = false;
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}
void doCallbacks(int callbackType, long frameTimeNanos) {
// 获取当前类型 Callback 链表的首部节点
CallbackRecord callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
try {
// 遍历链表,执行任务。比如执行 ViewRootImpl#TraversalRunnable 任务。
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
// 遍历链表,重置 Callback
}
}
}
scheduleVsync() 方法通过 native 方法 nativeScheduleVsync() 向 SurfaceFlinger 服务注册,即在下一次脉冲接收后会调用 DisplayEventReceiver 的 dispatchVsync() 方法;
底层向应用层发送VSYNC信号,java 层通过 dispatchVsync() 接收,最后回调在FrameDisplayEventReceiver#onVsync();
处理绘制任务
// ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
//获取Choreographer实例。useSfChoreographer 默认 false。
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 开始测量,布局和绘制流程
performTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 同步屏障的作用是可以拦截 Looper 对同步消息的获取和分发。
// 加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
VSync 机制
Android 系统每隔 16ms 发出 VSYNC 信号,触发对UI进行渲染,VSync是Vertical Synchronization(垂直同步)的缩写。
- 16ms 内只会申请一次垂直同步信号;
- VSync信号由SurfaceFlinger实现并定时发送。
总结
- Choreographer 的职责是统一处理输入、绘制、动画和提交任务;
- 主线程通过 Choreographer 把不同类型的 Callback 添加到单链表中。时间到后,根据 Callback 优先级遍历各个单链表执行任务;
参考
[1] 屏幕刷新机制Choreographer原理分析
[2] Android屏幕刷新机制解析
[3] Android 屏幕刷新机制与Choreographer全面解读