/** * 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; private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
Choreographer, 黄油计划引入的协调器,实现了帧(就是Frame,因为有了垂直同步,所以可能Frame_callback是定期的)同步的功能, 负责同意协调所有的task的运行。
Choreographer 所支持的三种Task, 运行顺序: input > animation > traversal.
Choreographer 的getInstance() 返回的是 ThreadLocal的实例, 每个线程都有自己的Choreographer, 并且Choreographer使用的handler所依附也是该线程:
可以看到 Looper.myLooper()
// Thread local storage for the 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!"); } return new Choreographer(looper); } }; <p>private Choreographer(Looper looper) { mLooper = looper; mHandler = new FrameHandler(looper);</p><p>..................... </p><p>}</p>
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; } } }注意上面的三类msg,处理的逻辑是不一样的:
doScheduleVsync() 和 doScheduleCallback()都没有真正的干活,
void doScheduleCallback(int callbackType) { synchronized (mLock) { if (!mFrameScheduled) { final long now = SystemClock.uptimeMillis(); if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { scheduleFrameLocked(now); } } } }
比如doScheduleCallback: 先检查有没有Frame的CallBack已经被schedue了<如果有的话,在不久这些被post进来的task就可以被执行了,不需要再schedule一次>,如果没有,再看看当前的callBackQueue的第一个callback是否已经到期要执行.
如果到期要执行,那么就schedule一个 Frame的Callback:
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; 如果使用了V同步: if (USE_VSYNC) { if (DEBUG) { Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. 如果当前线程就是该Choreographer所属的线程 if (isRunningOnLooperThreadLocked()) { 直接schedule一个V同步. scheduleVsyncLocked(); 做的事情: <mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; <pre name="code" class="html"> mDisplayEventReceiver.scheduleVsync();>
/** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ 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); } } onVsync()是收到了Vsync pulse之后的响应: /** * Called when a vertical sync pulse is received. * The recipient should render a frame and then call {@link #scheduleVsync} * to schedule the next vertical sync pulse. * * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()} * timebase. * @param builtInDisplayId The surface flinger built-in display id such as * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}. * @param frame The frame number. Increases by one for each vertical sync interval. */ public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { }
而Vsync里传递来的timestampNanos,就是本次pulse的时间戳。
而FrameDisplayEventReceiver的onVsync()函数做的事情是将作为runnable的自己send到所处的线程中去,注意是sendMessageAtTime,这里的时间是
这样计算的,如果timestampNanos > now, 那么timestampNanos = now.
Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
而doFrame()函数才是真正的做了很多工作:
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { 如果没有schedule过一个FrameCallBack,直接返回.mFrameScheduled只有在scheduleFrameLocked()才会是true if (!mFrameScheduled) { return; // no work to do } startNanos = System.nanoTime(); 计算一下抖动延迟:startNanos 是当前的时间,而frameTimeNanos是该Frame计划开始的时间 final long jitterNanos = startNanos - frameTimeNanos; 如果抖动延迟超过了某个阈值 <mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());> if (jitterNanos >= mFrameIntervalNanos) { 大概计算一下跳帧的严重程度 final long skippedFrames = jitterNanos / mFrameIntervalNanos; 如果跳帧跳的不能忍了,警告一下,是不是UI线程的工作太多了. if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread."); } 取余以直接略过之前跳的帧. final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; if (DEBUG) { Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + "which is more than the frame interval of " + (mFrameIntervalNanos * 0.000001f) + " ms! " + "Skipping " + skippedFrames + " frames and setting frame " + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); } 重新规划一下该Frame开始的时间 frameTimeNanos = startNanos - lastFrameOffset; } 如果这一帧要开始的时间比上一帧开始的时间都晚,那么直接跳过此次,等待下一次的Vsync来触发了. 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; } 这一帧开始了,那么就没有被schedule的帧了 mFrameScheduled = false; 更新上一帧开始的时间 mLastFrameTimeNanos = frameTimeNanos; } 这时候才开始依次执行之前post给自己的各种Task,注意执行的顺序就是最开始说的顺序: doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); 打印出画这一帧所花的时间: if (DEBUG) { final long endNanos = System.nanoTime(); Log.d(TAG, "Frame " + frame + ": Finished, took " + (endNanos - startNanos) * 0.000001f + " ms, latency " + (startNanos - frameTimeNanos) * 0.000001f + " ms."); } }
postCallbackDelayedInternal负责把MSG_DO_SCHEDULE_CALLBACK的Task, post到该Choreographer实例所属线程的handler中, 通过Message的形式,这个函数其实也是是view的requestLayout等操作,最终使ViewRootImpl调用的:
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); } } }
关于这个类注释说的也很清晰了:
/** * Coordinates the timing of animations, input and drawing. * <p> * 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. * </p><p> * Applications typically interact with the choreographer indirectly using * higher level abstractions in the animation framework or the view hierarchy. * Here are some examples of things you can do using the higher-level APIs. * </p> * <ul> * <li>To post an animation to be processed on a regular time basis synchronized with * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li> * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display * frame, use {@link View#postOnAnimation}.</li> * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display * frame after a delay, use {@link View#postOnAnimationDelayed}.</li> * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the * next display frame, use {@link View#postInvalidateOnAnimation()} or * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li> * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in * sync with display frame rendering, do nothing. This already happens automatically. * {@link View#onDraw} will be called at the appropriate time.</li> * </ul> * <p> * However, there are a few cases where you might want to use the functions of the * choreographer directly in your application. Here are some examples. * </p> * <ul> * <li>If your application does its rendering in a different thread, possibly using GL, * or does not use the animation framework or view hierarchy at all * and you want to ensure that it is appropriately synchronized with the display, then use * {@link Choreographer#postFrameCallback}.</li> * <li>... and that's about it.</li> * </ul> * <p> * Each {@link Looper} thread has its own choreographer. Other threads can * post callbacks to run on the choreographer but they will run on the {@link Looper} * to which the choreographer belongs. * </p> */
最终还是一个Vsync触发了doFrame,来进行操作的. 不过如果之前已经有一个Frame将要执行了,那么就不需要再schedule一个,直接等待这个Frame的来临即可.
From:http://www.360doc.com/content/14/0329/00/10366845_364576441.shtml
所有的图像显示输出都是由时钟驱动的,这个驱动信号称为VSYNC。这个名词来源于模拟电视时代,在那个年代,因为带宽的限制,每一帧图像都有分成两次传输,先扫描偶数行(也称偶场)传输,再回到头部扫描奇数行(奇场),扫描之前,发送一个VSYNC同步信号,用于标识这个这是一场的开始。场频,也就是VSYNC 频率决定了帧率(场频/2). 在现在的数字传输中,已经没有了场的概念,但VSYNC这一概念得于保持下来,代表了图像的刷新频率,意味着收到VSYNC信号后,我们必须将新的一帧进行显示。
VSYNC一般由硬件产生,也可以由软件产生(如果够准确的话),Android 中VSYNC来着于HWComposer,接收者没错,就是Choreographer。Choreographer英文意思是编舞者,跳舞很讲究节奏不是吗,必须要踩准点。Choreographer 就是用来帮助Android的动画,输入,还是显示刷新按照固定节奏来完成工作的。
从图中我们可以看到, Choreographer 是ViewRootImpl 创建的(Choreographer是一个sigleton类,第一个访问它的ViewRootImpl创建它),它拥有一个Receiver, 用来接收外部传入的Event,它还有一个Callback Queue, 里面存放着若干个CallbackRecord, 还有一个FrameHandler,用来handleMessage, 最后,它还跟Looper有引用关系。再看看下面这张时序图,一切就清楚了,
首先Looper调用loop() 后,线程进入进入睡眠,直到收到一个消息。Looper也支持addFd()方法,这样如果某个fd上发生了IO操作(read/write), 它也会从睡眠中醒来。Choreographer的实现用到了这两种方式,首先他通过某种方式获取到SurfaceFlinger 进程提供的fd,然后将其交给Looper进行监听,只要SurfaceFlinger往这个fd写入VSync事件,looper便会唤醒。Lopper唤醒后,会执行onVsync()时间,这里面没有做太多事情,而是调用Handler接口 sendMessageAtTime() 往消息队列里又送了一个消息。这个消息最终调到了Handler (实际是FrameHandler)的handleCallback来完成上层安排的工作。为什么要绕这么大个圈?为什么不在onVSync里直接handleCallback()? 毕竟onVSync 和 handleCallback() 都在一个线程里。这是因为MessageQueue 不光接收来自SurfaceFlinger 的VSync 事件,还有来自上层的控制消息。VSync的处理是相当频繁的,如果不将VSync信号送人MessageQueue进行排队,MessageQueue里的事件就有可能得不到及时处理,严重的话会导致溢出。当然了,如果因为VSync信号排队而导致处理延迟,这就是设计的问题了,这也是为什么Android文档里反复强调在Activity的onXXX()里不要做太耗时的工作,因为这些回调函数和Choreographer运行在同一个线程里,这个线程就是所谓的UI线程。
言归正传,继续往前,VSync事件最终在doFrame()里调了三次doCallbacks()来完成不同的功能, 分别处理用户输入事件,动画刷新(动画就是定时更新的图片), 最后执行performTraversals(),这个函数里面主要是检查当前窗口当前状态,比如说是否依然可见,尺寸,方向,布局是否发生改变(可能是由前面的用户输入触发的),分别调用performMeasure(), performLayout, performDraw()完成测量,布局和绘制工作。