引言
之前其实并未关注过Choreographer,在一次调试App demo的过程中,偶然发现出现了一条这样的日志:
I/Choreographer: Skipped 1201 frames! The application may be doing too much work on its main thread.
这是一条系统日志,意思很明确:主线程的工作可能过多,导致了掉帧。突然发现Choreographer很有用,可以用来监控App性能、卡顿、帧率等,于是决定花点时间学习一下。
简介
Choreographer类位于android.view包下,是一个final类,是从Android 4.1(API level=16)开始加入的一种机制。Choreographer字面意为“编舞、编导”,从官方文档可以得到以下重要信息:
- 协调动画、输入和绘图的时间。
- Choreographer从显示子系统接收定时脉冲,如VSYNC信号,然后调度安排下一帧的渲染工作。
- 应用程序一般不与Choreographer直接交互,而是在动画框架或视图层次架构中使用更高层的抽象API来调用。
- 也有一些在App中直接使用Choreographer的场景,如App在不同的线程进行渲染,可能使用GL,没有使用动画框架或视图层次架构,当需要确保能够和显示同步的时候,可以调用Choreographer的
postFrameCallback(Choreographer.FrameCallback)
方法。 - 每一个Looper线程都有自己的Choreographer,其他线程发送的回调只能运行在对应Choreographer所属的Looper线程上。
源码分析
Choreographer源码基于Android 7.1.1,不同版本源码可能有所不同。
类声明
public final class Choreographer{ }
构造函数
private Choreographer(Looper looper) { mLooper = looper; mHandler = new FrameHandler(looper); // 根据是否使用了VSYNC来创建一个FrameDisplayEventReceiver对象 mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; // 1秒=1000毫秒=1000000微秒=1000000000纳秒,mFrameIntervalNanos为帧时间间隔,单位纳秒 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); // CALLBACK_LAST + 1 = 4,创建一个容量为4的CallbackQueue数组,用来存放4种不同的Callback mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }
Choreographer类中有一个Looper和一个FrameHandler变量。FrameHandler继承自Handler,了解Android消息机制的自然知道Handler需要Looper来调度消息进行处理。
FrameHandler是Choreographer的一个内部类,其定义如下:
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: // 请求VSYNC信号 doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; } } }
FrameHandler的实现非常简单,只处理三类消息,具体的常量标识为:
private static final int MSG_DO_FRAME = 0; private static final int MSG_DO_SCHEDULE_VSYNC = 1; private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
下面代码创建一个容量为4的CallbackQueue数组,用来存放4种不同的Callback。
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); }
具体是哪四种CallBack,通过如下代码可知即Input callback、Animation callback、Traversal callback、Commit callback,分别表示输入、动画、布局绘制、提交操作。
/** * Callback type: Input callback. Runs first. * @hide */ public static final int CALLBACK_INPUT = 0; /** * Callback type: Animation callback. Runs before traversals. * @hide */ public static final int CALLBACK_ANIMATION = 1; /** * Callback type: Traversal callback. Handles layout and draw. Runs last * after all other asynchronous messages have been handled. * @hide */ public static final int CALLBACK_TRAVERSAL = 2; /** * Callback type: Commit callback. Handles post-draw operations for the frame. * Runs after traversal completes. The {@link #getFrameTime() frame time} reported * during this callback may be updated to reflect delays that occurred while * traversals were in progress in case heavy layout operations caused some frames * to be skipped. The frame time reported during this callback provides a better * estimate of the start time of the frame in which animations (and other updates * to the view hierarchy state) actually took effect. * @hide */ public static final int CALLBACK_COMMIT = 3; // 这一类型是在API level=23的时候添加的
获取Choreographer实例
/** * Gets the choreographer for the calling thread. Must be called from * a thread that already has a {@link android.os.Looper} associated with it. * * @return The choreographer for this thread. * @throws