Choreographer 翩翩起舞

来源:互联网 发布:数据恢复大师靠谱吗 编辑:程序博客网 时间:2024/04/29 08:53
    /**     * 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>

FrameHandler 实现了自己的handleMessage:

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();
>
} else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else {
否则就自己计算出下一个Frame的时间,然后send一个MSG_DO_FRAME的message:
final long nextFrameTime = Math.max( mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now); if (DEBUG) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg是异步的,不依赖严格的先后顺序.
  msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }

上面的FrameDisplayEventReceiver是一个实现了Runnable继承DisplayEventReceiver的类,其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) {    }

注意注释:
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.

而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);

FrameDisplayEventReceiver的run函数则是直接执行了doFrame():

而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.");        }    }

doCallbacks函数做的事情就简单了,就是把相应Task type的mCallbackQueues里已经到期的所有callBack执行一遍.


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> */

Choreographer应该是由两种触发来运行doFrame的,一种可能是系统通过Vsync触发的<猜的,不一定,因为scheduleFrameLocked是一个private函数>,另外一种就是通过postCallback()触发了一个scheduleVsync,

最终还是一个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()完成测量,布局和绘制工作。

0 0
原创粉丝点击