结论写在前面:Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种事件在消息输入、加入动画、准备绘图layout 等动作时均会发给Choreographer。
下面来看分析过程
看过一些源码后,发现ui 绘制的管理,调度都是通过Choreographer这个类。
1 Choreographer 是什么?有什么?
Choreographer 是个普通类,final 表示不能被继承修改其行为。
- public final class Choreographer
- private static final ThreadLocal<Choreographer> sThreadInstance =
- new ThreadLocal<Choreographer>()
- protected Choreographer initialValue() {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalStateException("The current thread must have a looper!");
- }
- return new Choreographer(looper);
- }
- private final FrameHandler mHandler;
- 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;
- }
- }
- }
查一下概念,的确符合,DO_FRAME 是帧刷新,SCHEDULE_VSYNC是 垂直同步刷新。
在android4.1上加入的VSYNC 特性,并使用三重缓冲大幅改善了android 图像方面的性能。
- private void scheduleFrameLocked(long now) {
- if (USE_VSYNC) {
- if (isRunningOnLooperThreadLocked()) {
- scheduleVsyncLocked();
- } else {
- Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtFrontOfQueue(msg);
- }
- } else {
- Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, nextFrameTime);
- }
2 从VSYNC 开始case MSG_DO_SCHEDULE_VSYNC:
- void doScheduleVsync() {
- synchronized (mLock) {
- if (mFrameScheduled) {
- scheduleVsyncLocked();
- }
- }
- }
- private void scheduleVsyncLocked() {
- mDisplayEventReceiver.scheduleVsync();
- }
- public void scheduleVsync() {
- if (mReceiverPtr == 0) {
- Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
- + "receiver has already been disposed.");
- } else {
- nativeScheduleVsync(mReceiverPtr);
- }
- }
native的东西先不往下看,回头来看mDisplayEventReceiver
- private final class FrameDisplayEventReceiver extends DisplayEventReceiver
- implements Runnable
- // Called from native code.
- @SuppressWarnings("unused")
- private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
- onVsync(timestampNanos, builtInDisplayId, frame);
- }
- @Override
- public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
- mTimestampNanos = timestampNanos;
- mFrame = frame;
- Message msg = Message.obtain(mHandler, this);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
- @Override
- public void run() {
- mHavePendingVsync = false;
- doFrame(mTimestampNanos, mFrame);
- }
- void doFrame(long frameTimeNanos, int frame) {
- synchronized (mLock) {
- //省略一些赋值
- //可能时跳帧的情况,直接调用vsync,并return
- if (frameTimeNanos < mLastFrameTimeNanos) {
- if (DEBUG) {
- Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
- + "previously skipped frame. Waiting for next vsync.");
- }
- scheduleVsyncLocked();
- return;
- }
- }
- //先后处理事件回调、动画回调、绘制回调
- doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
- doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
- doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
- }
否则开始新的一帧的绘制,依次处理事件回调、动画回调、绘制回调
在doCallbacks() 中先根据当前时间去除队列中第一个有效的回调,然后依次处理这些回调。
- void doCallbacks(int callbackType, long frameTimeNanos) {
- CallbackRecord callbacks;
- synchronized (mLock) {
- // We use "now" to determine when callbacks become due because it's possible
- // for earlier processing phases in a frame to post callbacks that should run
- // in a following phase, such as an input event that causes an animation to start.
- final long now = SystemClock.uptimeMillis();
- <span style="background-color: rgb(102, 255, 255);">//按时间取队列头</span>
- callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
- if (callbacks == null) {
- return;
- }
- mCallbacksRunning = true;
- }
- try {
- for (CallbackRecord c = callbacks; c != null; c = c.next) {
- if (DEBUG) {
- Log.d(TAG, "RunCallback: type=" + callbackType
- + ", action=" + c.action + ", token=" + c.token
- + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
- }
- <span style="background-color: rgb(51, 255, 255);">//回调事件
- </span> c.run(frameTimeNanos);
- }
- } finally {
- synchronized (mLock) {
- mCallbacksRunning = false;
- do {
- final CallbackRecord next = callbacks.next;
- recycleCallbackLocked(callbacks);
- callbacks = next;
- } while (callbacks != null);
- }
- }
- }
回调的数据结构是CallbackRecord,一个单向链表。根据token 的标志,来回调doframe 或者runnable
- private static final class CallbackRecord {
- public CallbackRecord next;
- public long dueTime;
- public Object action; // Runnable or FrameCallback
- public Object token;
- public void run(long frameTimeNanos) {
- if (token == FRAME_CALLBACK_TOKEN) {
- ((FrameCallback)action).doFrame(frameTimeNanos);
- } else {
- ((Runnable)action).run();
- }
- }
- }
3. 哪里会使用Choreographer?
在《Android 动画animation 深入分析》http://blog.csdn.net/farmer_cc/article/details/18259117中分析到scheduleAnimation 的时候就是调用的android.view.Choreographer.postCallback(int, Runnable, Object) 方法。查看该方法的调用,在UI 绘制、和其他和动画相关的类中均有调用。这里并没有全部列出来。
scheduleTraversals() : void - android.view.ViewRootImpl
scheduleAnimation() : void - android.animation.ValueAnimator.AnimationHandler
scheduleAnimationLocked() : void - com.android.server.wm.WindowManagerService
postOnAnimation(Runnable) : void - android.view.View
在postCallbackDelayedInternal()中 把新来的事件加入到队列中,并根据时间来判断是去frame 还是直接回调
- private void postCallbackDelayedInternal(int callbackType,
- Object action, Object token, long delayMillis) {
- if (DEBUG) {
- Log.d(TAG, "PostCallback: type=" + callbackType
- + ", action=" + action + ", token=" + token
- + ", delayMillis=" + delayMillis);
- }
- synchronized (mLock) {
- final long now = SystemClock.uptimeMillis();
- final long dueTime = now + delayMillis;
- <span style="background-color: rgb(51, 255, 255);">// 入队列</span>
- mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
- if (dueTime <= now) {
- scheduleFrameLocked(now);
- } else {
- <span style="background-color: rgb(51, 255, 255);">// 直接回调</span>
- Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
- msg.arg1 = callbackType;
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, dueTime);
- }
- }
至此,事件的来龙去脉就明确了。Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种事件在消息输入、加入动画、准备绘图layout 等动作时均会发给Choreographer。