深入分析UI 上层事件处理核心机制 Choreographer
结论写在前面: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>()该对象必须持有looper,意味着将使用消息队列
protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } return new Choreographer(looper); }持有一个handler 对象
private final FrameHandler mHandler;这个handler对象仅处理3种事件:
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; } } }在scheduleFrameLocked中事件被静态变量USE_VSYNC分开,也就是系统仅使用MSG_DO_SCHEDULE_VSYNC或MSG_DO_FRAME。
查一下概念,的确符合,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); } }一路调用到nativeScheduleVsync();
native的东西先不往下看,回头来看mDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable观察一下方法名都是onXX ,一看就是回调的节奏
// Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) { onVsync(timestampNanos, builtInDisplayId, frame); }看到这里的注释,就明确了这个类就是被native回调了。回调时调用onVsync();而这个方法时在FrameDisplayEventReceiver中重写的。
@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); }在onsync中把自己的runnable 加到消息队列中执行,这里使用异步消息,调用绘制do_frame()方法。
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); }在do_frame()中判断有跳帧可能性,直接继续vsync。
否则开始新的一帧的绘制,依次处理事件回调、动画回调、绘制回调
在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(); //按时间取队列头 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)); } //回调事件 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(); } } }从这里可以看到,处理的事件分为两类:一类是doframe 事件,另一类是runnable 方法。接着,就来看一下到底是加的这些事件。
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; // 入队列 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } else { // 直接回调 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。
写在后面:结合前文《Android 动画animation 深入分析》http://blog.csdn.net/farmer_cc/article/details/18259117中可以更多了解动画相关内容