美文网首页
android编舞者ChoreoGrapher

android编舞者ChoreoGrapher

作者: 嘻嘻疯子 | 来源:发表于2019-04-22 15:35 被阅读0次

    Choreographer的作用

    结合上篇Android 绘制原理可知道,屏幕每16ms 显示frame buffer上的帧信息,然后frame buffer和back buffer进行互换,并发送vsync信号,此时CPU会中断其它操作,来通过ChoreGrapher去确定显示的帧信息(控件应该怎么画,画在哪个位置,画多大),当这些信息确定后,GPU再根据这些信息刷新back buffer的信息,刷新完成后 back buffer 和 frame buffer互换位置。

    Choreographer的具体运行过程

    Choreographer主要做如下工作,下面我们逐一分析

    • 注册监听,当收到vsync信号,会回调
    • 接受并保存控制帧的信息的任务
    • 调用4个回调链表确定帧信息

    源码分析

    注册监听-FrameDisplayEventReceiver

    FrameDisplayEventReceiver是ChoreoGrapher的私有类,当收到vsync信号,且c/c++回调开关是打开的会调用FrameDisplayEventReceiver的onVsync()。
    FrameDisplayEventReceiver在Choreographer被创建,具体在第4行:

    //frameworks/base/core/java/android/view/Choreographer.java
    
    private Choreographer(Looper looper) {
            mLooper = looper;
            mHandler = new FrameHandler(looper);
            mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
            mLastFrameTimeNanos = Long.MIN_VALUE;
    
            mFrameIntervalNanos = (long) (1000000000 / getRefreshRate());
    
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
        }
    

    FrameDisplayEventReceiver继承自DisplayEventReceiver,我们跟进去看构造方法,会发现调用了nativeInit(),这是个native方法:

    frameworks/base/core/java/android/view/DisplayEventReceiver.java
    
    /**
         * Creates a display event receiver.
         *
         * @param looper The looper to use when invoking callbacks.
         */
        public DisplayEventReceiver(Looper looper) {
            if (looper == null) {
                throw new IllegalArgumentException("looper must not be null");
            }
    
            mMessageQueue = looper.getQueue();
            mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);
    
            mCloseGuard.open("dispose");
        }
    

    通过FrameDisplayEventReceiver就是通过这个nativeInit()方法注册进去的。
    在nativeInit()中将我们java层传过来的DisplayEventReceiver和mMessageQueue 作为参数new了个NativeDisplayEventReceiver对象,然后调用NativeDisplayEventReceiver->initialize().

    //frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
    
    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
            jobject messageQueueObj) {
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        if (messageQueue == NULL) {
            jniThrowRuntimeException(env, "MessageQueue is not initialized.");
            return 0;
        }
    
        sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
                receiverWeak, messageQueue);
        status_t status = receiver->initialize();
        if (status) {
            String8 message;
            message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
            jniThrowRuntimeException(env, message.string());
            return 0;
        }
    

    我们找到NativeDisplayEventReceiver构造方法

    frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
    
    NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
            jobject receiverWeak, const sp<MessageQueue>& messageQueue) :
            DisplayEventDispatcher(messageQueue->getLooper()),
            mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
            mMessageQueue(messageQueue), mWaitingForVsync(false) {
        ALOGV("receiver %p ~ Initializing display event receiver.", this);
    }
    

    通过下面代码知道NativeDisplayEventReceiver是继承自DisplayEventDispatcher的,而NativeDisplayEventReceiver中没有initialize(),于是我们知道status_t status = receiver->initialize();调用的是NativeDisplayEventReceiver的initialize():

    //frameworks\base\libs\androidfw\DisplayEventDispatcher.cpp
    
    status_t DisplayEventDispatcher::initialize() {
        status_t result = mReceiver.initCheck();
        if (result) {
            ALOGW("Failed to initialize display event receiver, status=%d", result);
            return result;
        }
    
        int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
                this, NULL);
        if (rc < 0) {
            return UNKNOWN_ERROR;
        }
        return OK;
    }
    

    DisplayEventDispatcher是继承自LooperCallback 的,在DisplayEventDispatcher::initialize()中调用了

      int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
                this, NULL);
    

    这句话的意思是监听mReceiver,一旦有数据到来则回调this(此处DisplayEventDispatcher)中所复写LooperCallback对象的 handleEvent。

    //frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
    
    int DisplayEventDispatcher::handleEvent(int, int events, 
        ... ...
        // Drain all pending events, keep the last vsync.
        nsecs_t vsyncTimestamp;
        int32_t vsyncDisplayId;
        uint32_t vsyncCount;
         //清除所有的pending事件,只保留最后一次vsync
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d",
                    this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
            mWaitingForVsync = false;
            //分发Vsync
            dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
        }
    
        return 1; // keep the callback
    }
    

    handleEvent()倒数第5行会调用到NativeDisplayEventReceiver的复写的dispatchVsync():

    frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
    
    void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
    
        ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
        if (receiverObj.get()) {
            ALOGV("receiver %p ~ Invoking vsync handler.", this);
            //此处调用到Java层的DisplayEventReceiver对象的dispatchVsync()方法
            env->CallVoidMethod(receiverObj.get(),
                    gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
            ALOGV("receiver %p ~ Returned from vsync handler.", this);
        }
    
        mMessageQueue->raiseAndClearException(env, "dispatchVsync");
    }
    

    调用4个回调链表确定帧信息

    在NativeDisplayEventReceiver::dispatchVsync通过如下代码调用java层DisplayEventReceiver的dispatchVsync().

       env->CallVoidMethod(receiverObj.get(),
                    gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
    

    我们回到NativeDisplayEventReceiver.dispatchVsync():

       //android.view.DisplayEventReceiver
    
       // Called from native code.
        @SuppressWarnings("unused")
        private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
            onVsync(timestampNanos, builtInDisplayId, frame);
        }
    

    Choreographer对象实例化的过程,创建的对象是DisplayEventReceiver子类 FrameDisplayEventReceiver对象,我们进入FrameDisplayEventReceiver复写的onVsync方法:

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
    
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            //忽略来自第二显示屏的Vsync
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                scheduleVsync();
                return;
            }
            ...
            
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //该消息的callback为当前对象FrameDisplayEventReceiver
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //此处mHandler为FrameHandler
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
    
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame); //【见小节2.8】
        }
    }
    

    可见onVsync()中是通过FrameHandler向主线程Looper发送了一个自带callback的消息, callback为FrameDisplayEventReceiver。 当主线程Looper执行到该消息时,则调用FrameDisplayEventReceiver.run(),紧接着便是调用doFrame()

    //android.view.Choreographer
    
    void doFrame(long frameTimeNanos, int frame) {
            final long startNanos;
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    return; // no work to do
                }
    
                if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
                    mDebugPrintNextFrameTimeDelta = false;
                    Log.d(TAG, "Frame time delta: "
                            + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
                }
    
                long intendedFrameTimeNanos = frameTimeNanos;
                startNanos = System.nanoTime();
                final long jitterNanos = startNanos - frameTimeNanos;
                if (jitterNanos >= mFrameIntervalNanos) {
                    final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                    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_JANK) {
                        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.");
                    }
                    frameTimeNanos = startNanos - lastFrameOffset;
                }
    
                if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    scheduleVsyncLocked();
                    return;
                }
    
                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
                mFrameScheduled = false;
                mLastFrameTimeNanos = frameTimeNanos;
            }
    
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
                mFrameInfo.markInputHandlingStart();
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
                mFrameInfo.markAnimationsStart();
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    
                mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
            } finally {
                AnimationUtils.unlockAnimationClock();
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
            if (DEBUG_FRAMES) {
                final long endNanos = System.nanoTime();
                Log.d(TAG, "Frame " + frame + ": Finished, took "
                        + (endNanos - startNanos) * 0.000001f + " ms, latency "
                        + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
            }
        }
    

    每调用一次scheduleFrameLocked(),则mFrameScheduled为true,能执行一次 doFrame()操作。doframe的大致流程如下:


    image.png

    总结起来其实主要是三个操作:

    1.设置当前frame的启动时间。
    判断是否跳帧,若跳帧修正当前frame的启动时间到最近的VSync信号时间。如果没跳帧,当前frame启动时间直接设置为当前VSync信号时间。修正完时间后,无论当前frame是否跳帧,使得当前frame的启动时间与VSync信号还是在一个节奏上的,可能可能延后了一到几个周期,但是节奏点还是吻合的。
    如下图所示是时间修正的一个例子,


    image.png

    由于第二个frame执行超时,第三个frame实际启动时间比第三个VSync信号到来时间要晚,因为这时候延时比较小,没有超过一个时钟周期,系统还是将frameTimeNanos3传给回调,回调拿到的时间和VSync信号同步。
    再来看看下图:


    image.png
    由于第二个frame执行时间超过2个时钟周期,导致第三个frame延后执行时间大于一个时钟周期,系统认为这时候影响较大,判定为跳帧了,将第三个frame的时间修正为frameTimeNanos4,比VSync真正到来的时间晚了一个时钟周期。
    时间修正,既保证了doFrame操作和VSync保持同步节奏,又保证实际启动时间与记录的时间点相差不会太大,便于同步及分析。
    2.判断是否事件回溯
    在doframe中 如下代码判断是否时间回溯:
      if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    scheduleVsyncLocked();
                    return;
                }
    
                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
                mFrameScheduled = false;
                mLastFrameTimeNanos = frameTimeNanos;
    

    我们在看下FrameDisplayEventReceiver :

     private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
           ···· ···
            @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 / TimeUtils.NANOS_PER_MS);
            }
    
            @Override
            public void run() {
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame);
            }
        }
    

    可知是通过handler发送message, 然后走run方法执行doFrame的。
    同样看下图


    a.png

    由于第二个frame执行时间超过3个时钟周期,导致第三个frame延后执行时间大于两个个时钟周期,系统认为这时候影响较大,判定为跳帧了,将第三个frame的时间修正为frameTimeNanos5,比VSync真正到来的时间晚了一个时钟周期。当图中第5个vsync到来时,消息队列中还有3个任务(取出frameTimeNanos1和frameTimeNanos2所对应的任务,还剩frameTimeNanos3和frameTimeNanos4和frameTimeNanos5),当执行frameTimeNanos4所对应的任务时,此时通过frameTimeNanos为frameTimeNanos4 ,mLastFrameTimeNanos 为frameTimeNanos5的值,此时frameTimeNanos<mLastFrameTimeNanos ,即时间回溯。
    3.顺序执行callBack队列里面的callback.

    然后接下来看看doCallbacks的执行过程:

    //android.view.Choreographer
    
    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 = System.nanoTime();
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
    
                // Update the frame time if necessary when committing the frame.
                // We only update the frame time if we are more than 2 frames late reaching
                // the commit phase.  This ensures that the frame time which is observed by the
                // callbacks will always increase from one frame to the next and never repeat.
                // We never want the next frame's starting frame time to end up being less than
                // or equal to the previous frame's commit frame time.  Keep in mind that the
                // next frame has most likely already been scheduled by now so we play it
                // safe by ensuring the commit time is always at least one frame behind.
                if (callbackType == Choreographer.CALLBACK_COMMIT) {
                    final long jitterNanos = now - frameTimeNanos;
                    Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                    if (jitterNanos >= 2 * mFrameIntervalNanos) {
                        final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                                + mFrameIntervalNanos;
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                    + " ms which is more than twice the frame interval of "
                                    + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                    + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                    + " ms in the past.");
                            mDebugPrintNextFrameTimeDelta = true;
                        }
                        frameTimeNanos = now - lastFrameOffset;
                        mLastFrameTimeNanos = frameTimeNanos;
                    }
                }
            }
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        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);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    

    callback的类型有以下4种,除了文章一开始提到的3中外,还有一个CALLBACK_COMMIT。

    CALLBACK_INPUT:输入
    CALLBACK_ANIMATION:动画
    CALLBACK_TRAVERSAL:遍历,执行measure、layout、draw
    CALLBACK_COMMIT:遍历完成的提交操作,用来修正动画启动时间

    然后看上面的源码,分析一下每个callback的执行过程:

    1.callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS);得到执行时间在当前时间之前的所有CallBack,保存在单链表中。每种类型的callback按执行时间先后顺序排序分别存在一个单链表里面。为了保证当前callback执行时新post进来的callback在下一个frame时才被执行,这个地方extractDueCallbacksLocked会将需要执行的callback和以后执行的callback断开变成两个链表,新post进来的callback会被放到后面一个链表中。当前frame只会执行前一个链表中的callback,保证了在执行callback时,如果callback中Post相同类型的callback,这些新加的callback将在下一个frame启动后才会被执行。

    2.接下来,看一大段注释,如果类型是CALLBACK_COMMIT,并且当前frame渲染时间超过了两个时钟周期,则将当前提交时间修正为上一个垂直同步信号时间。为了保证下一个frame的提交时间和当前frame时间相差为一且不重复。
    这个地方注释挺难看懂,实际上这个地方CALLBACK_COMMIT是为了解决ValueAnimator的一个问题而引入的,主要是解决因为遍历时间过长导致动画时间启动过长,时间缩短,导致跳帧,这里修正动画第一个frame开始时间延后来改善,这时候才表示动画真正启动。为什么不直接设置当前时间而是回溯一个时钟周期之前的时间呢?看注释,这里如果设置为当前frame时间,因为动画的第一个frame其实已经绘制完成,第二个frame这时候已经开始了,设置为当前时间会导致这两个frame时间一样,导致冲突。详细情况请看官方针对这个问题的修改。Fix animation start jank due to expensive layout operations.

    如下图所示:

    image.png

    3.接下来就是调用c.run(frameTimeNanos);执行回调。
    例如,你可以写一个自定义的FPSFrameCallback继承自Choreographer.FrameCallback,实现里面的doFrame方法。

    public class FPSFrameCallback implements Choreographer.FrameCallback{
    @Override
      public void doFrame(long frameTimeNanos){
          //do something
      }
    }
    

    通过
    Choreographer.getInstance().postFrameCallback(new FPSFrameCallback());
    把你的回调添加到Choreographer之中,那么在下一个frame被渲染的时候就会回调你的callback,执行你定义的doFrame操作,这时候你就可以获取到这一帧的开始渲染时间并做一些自己想做的事情了。

    自此FrameDisplayEventReceiver注册及其回调完成

    接受并保存控制帧的信息的任务

    Choreographer维护了一个队列数组,这个数组有4个元素分别对应于

    • INPUT:输入事件
    • ANIMATION:动画
    • TRAVERSAL:窗口刷新
    • COMMIT:主要是为了处理动画的bug
      这四个添加的流程大致上是一致的,我们以TRAVERSAL进行说明,从ViewRootImp的scheduleTraversals()方法说起:
    //android.view.ViewRootImpl
    
    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    会发现调用了mChoreographer.postCallback():

      //android.view.Choreographer
     
        public void postCallback(int callbackType, Runnable action, Object token) {
            postCallbackDelayed(callbackType, action, token, 0);
        }
    

    继续看postCallbackDelayed()

    //android.view.Choreographer
        public void postCallbackDelayed(int callbackType,
                Runnable action, Object token, long delayMillis) {
            if (action == null) {
                throw new IllegalArgumentException("action must not be null");
            }
            if (callbackType < 0 || callbackType > CALLBACK_LAST) {
                throw new IllegalArgumentException("callbackType is invalid");
            }
    
            postCallbackDelayedInternal(callbackType, action, token, delayMillis);
        }
    

    继续看postCallbackDelayedInternal()

    //android.view.Choreographer
    private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG_FRAMES) {
                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队列         
                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);
                   //发送消息MSG_DO_SCHEDULE_CALLBACK                
                   mHandler.sendMessageAtTime(msg, dueTime);
                }
            }
        }
    
    

    在这里将callback添加到队列,如果 不是延时直接调用cheduleFrameLocked()方法,如果是则通过handler调用,FrameHandler 代码如下:

    private final class FrameHandler extends Handler {
    
        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); //【见小节3.5】
                    break;
            }
        }
    }
    

    doScheduleCallback():

    void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
                final long now = SystemClock.uptimeMillis();
                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                    scheduleFrameLocked(now); //【见小节3.6】
                }
            }
        }
    }
    

    发现调用的是scheduleFrameLocked():

    private void scheduleFrameLocked(long now) {
            if (!mFrameScheduled) {
                mFrameScheduled = true;
                if (USE_VSYNC) {
                    if (DEBUG_FRAMES) {
                        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.
                    if (isRunningOnLooperThreadLocked()) {
                        scheduleVsyncLocked();
                    } else {
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtFrontOfQueue(msg);
                    }
                } else {
                    final long nextFrameTime = Math.max(
                            mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                    }
                    Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextFrameTime);
                }
            }
        }
    

    该方法的功能:

    • 当运行在Looper线程,则立刻调度scheduleVsyncLocked();
    • 当运行在其他线程,则通过发送一个消息到Looper线程,然后再执行scheduleVsyncLocked();
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync(); //【见小节3.8】
    }
    

    mDisplayEventReceiver对象是在Choreographer的实例化过程所创建的。

    public void scheduleVsync() {
         if (mReceiverPtr == 0) {
            ...
         } else {
             nativeScheduleVsync(mReceiverPtr);
         }
    }
    

    至此分析大致流程是


    image.png

    找到nativeScheduleVsync所对应的c++文件

    //frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
    
    static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
        sp<NativeDisplayEventReceiver> receiver =
                reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
        status_t status = receiver->scheduleVsync();
        ··· ···
        }
    }
    

    因为NativeDisplayEventReceiver 继承DisplayEventReceiver,找到DisplayEventReceiver中scheduleVsync():

    //android.view.DisplayEventReceiver
    
    status_t DisplayEventDispatcher::scheduleVsync() {
            ··· ····
            status_t status = mReceiver.requestNextVsync();
            ··· ···
    }
    
    

    该方法的作用请求下一次Vsync信息处理。 当Vsync信号到来,由于mFrameScheduled=true,则继续CallbackRecord.run()方法。

    参考:
    Android的16ms和垂直同步以及三重缓存
    Choreographer原理
    Android Choreographer 源码分析

    相关文章

      网友评论

          本文标题:android编舞者ChoreoGrapher

          本文链接:https://www.haomeiwen.com/subject/vuvygqtx.html