android編舞者ChoreoGrapher

Choreographer的作用

結(jié)合上篇Android 繪制原理可知道,屏幕每16ms 顯示frame buffer上的幀信息,然后frame buffer和back buffer進(jìn)行互換,并發(fā)送vsync信號(hào),此時(shí)CPU會(huì)中斷其它操作,來(lái)通過(guò)ChoreGrapher去確定顯示的幀信息(控件應(yīng)該怎么畫(huà),畫(huà)在哪個(gè)位置,畫(huà)多大),當(dāng)這些信息確定后,GPU再根據(jù)這些信息刷新back buffer的信息,刷新完成后 back buffer 和 frame buffer互換位置。

Choreographer的具體運(yùn)行過(guò)程

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

  • 注冊(cè)監(jiān)聽(tīng),當(dāng)收到vsync信號(hào),會(huì)回調(diào)
  • 接受并保存控制幀的信息的任務(wù)
  • 調(diào)用4個(gè)回調(diào)鏈表確定幀信息

源碼分析

注冊(cè)監(jiān)聽(tīng)-FrameDisplayEventReceiver

FrameDisplayEventReceiver是ChoreoGrapher的私有類,當(dāng)收到vsync信號(hào),且c/c++回調(diào)開(kāi)關(guān)是打開(kāi)的會(huì)調(diào)用FrameDisplayEventReceiver的onVsync()。
FrameDisplayEventReceiver在Choreographer被創(chuàng)建,具體在第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,我們跟進(jìn)去看構(gòu)造方法,會(huì)發(fā)現(xiàn)調(diào)用了nativeInit(),這是個(gè)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");
    }

通過(guò)FrameDisplayEventReceiver就是通過(guò)這個(gè)nativeInit()方法注冊(cè)進(jìn)去的。
在nativeInit()中將我們java層傳過(guò)來(lái)的DisplayEventReceiver和mMessageQueue 作為參數(shù)new了個(gè)NativeDisplayEventReceiver對(duì)象,然后調(diào)用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構(gòu)造方法

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

通過(guò)下面代碼知道NativeDisplayEventReceiver是繼承自DisplayEventDispatcher的,而NativeDisplayEventReceiver中沒(méi)有initialize(),于是我們知道status_t status = receiver->initialize();調(diào)用的是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()中調(diào)用了

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

這句話的意思是監(jiān)聽(tīng)mReceiver,一旦有數(shù)據(jù)到來(lái)則回調(diào)this(此處DisplayEventDispatcher)中所復(fù)寫(xiě)LooperCallback對(duì)象的 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;
        //分發(fā)Vsync
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }

    return 1; // keep the callback
}

handleEvent()倒數(shù)第5行會(huì)調(diào)用到NativeDisplayEventReceiver的復(fù)寫(xiě)的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);
        //此處調(diào)用到Java層的DisplayEventReceiver對(duì)象的dispatchVsync()方法
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
        ALOGV("receiver %p ~ Returned from vsync handler.", this);
    }

    mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}

調(diào)用4個(gè)回調(diào)鏈表確定幀信息

在NativeDisplayEventReceiver::dispatchVsync通過(guò)如下代碼調(diào)用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對(duì)象實(shí)例化的過(guò)程,創(chuàng)建的對(duì)象是DisplayEventReceiver子類 FrameDisplayEventReceiver對(duì)象,我們進(jìn)入FrameDisplayEventReceiver復(fù)寫(xiě)的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) {
        //忽略來(lái)自第二顯示屏的Vsync
        if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
            scheduleVsync();
            return;
        }
        ...
        
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        //該消息的callback為當(dāng)前對(duì)象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); //【見(jiàn)小節(jié)2.8】
    }
}

可見(jiàn)onVsync()中是通過(guò)FrameHandler向主線程Looper發(fā)送了一個(gè)自帶callback的消息, callback為FrameDisplayEventReceiver。 當(dāng)主線程Looper執(zhí)行到該消息時(shí),則調(diào)用FrameDisplayEventReceiver.run(),緊接著便是調(diào)用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.");
        }
    }

每調(diào)用一次scheduleFrameLocked(),則mFrameScheduled為true,能執(zhí)行一次 doFrame()操作。doframe的大致流程如下:


image.png

總結(jié)起來(lái)其實(shí)主要是三個(gè)操作:

1.設(shè)置當(dāng)前frame的啟動(dòng)時(shí)間。
判斷是否跳幀,若跳幀修正當(dāng)前frame的啟動(dòng)時(shí)間到最近的VSync信號(hào)時(shí)間。如果沒(méi)跳幀,當(dāng)前frame啟動(dòng)時(shí)間直接設(shè)置為當(dāng)前VSync信號(hào)時(shí)間。修正完時(shí)間后,無(wú)論當(dāng)前frame是否跳幀,使得當(dāng)前frame的啟動(dòng)時(shí)間與VSync信號(hào)還是在一個(gè)節(jié)奏上的,可能可能延后了一到幾個(gè)周期,但是節(jié)奏點(diǎn)還是吻合的。
如下圖所示是時(shí)間修正的一個(gè)例子,


image.png

由于第二個(gè)frame執(zhí)行超時(shí),第三個(gè)frame實(shí)際啟動(dòng)時(shí)間比第三個(gè)VSync信號(hào)到來(lái)時(shí)間要晚,因?yàn)檫@時(shí)候延時(shí)比較小,沒(méi)有超過(guò)一個(gè)時(shí)鐘周期,系統(tǒng)還是將frameTimeNanos3傳給回調(diào),回調(diào)拿到的時(shí)間和VSync信號(hào)同步。
再來(lái)看看下圖:


image.png

由于第二個(gè)frame執(zhí)行時(shí)間超過(guò)2個(gè)時(shí)鐘周期,導(dǎo)致第三個(gè)frame延后執(zhí)行時(shí)間大于一個(gè)時(shí)鐘周期,系統(tǒng)認(rèn)為這時(shí)候影響較大,判定為跳幀了,將第三個(gè)frame的時(shí)間修正為frameTimeNanos4,比VSync真正到來(lái)的時(shí)間晚了一個(gè)時(shí)鐘周期。
時(shí)間修正,既保證了doFrame操作和VSync保持同步節(jié)奏,又保證實(shí)際啟動(dòng)時(shí)間與記錄的時(shí)間點(diǎn)相差不會(huì)太大,便于同步及分析。
2.判斷是否事件回溯
在doframe中 如下代碼判斷是否時(shí)間回溯:
  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;

我們?cè)诳聪翭rameDisplayEventReceiver :

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

可知是通過(guò)handler發(fā)送message, 然后走run方法執(zhí)行doFrame的。
同樣看下圖


a.png

由于第二個(gè)frame執(zhí)行時(shí)間超過(guò)3個(gè)時(shí)鐘周期,導(dǎo)致第三個(gè)frame延后執(zhí)行時(shí)間大于兩個(gè)個(gè)時(shí)鐘周期,系統(tǒng)認(rèn)為這時(shí)候影響較大,判定為跳幀了,將第三個(gè)frame的時(shí)間修正為frameTimeNanos5,比VSync真正到來(lái)的時(shí)間晚了一個(gè)時(shí)鐘周期。當(dāng)圖中第5個(gè)vsync到來(lái)時(shí),消息隊(duì)列中還有3個(gè)任務(wù)(取出frameTimeNanos1和frameTimeNanos2所對(duì)應(yīng)的任務(wù),還剩frameTimeNanos3和frameTimeNanos4和frameTimeNanos5),當(dāng)執(zhí)行frameTimeNanos4所對(duì)應(yīng)的任務(wù)時(shí),此時(shí)通過(guò)frameTimeNanos為frameTimeNanos4 ,mLastFrameTimeNanos 為frameTimeNanos5的值,此時(shí)frameTimeNanos<mLastFrameTimeNanos ,即時(shí)間回溯。
3.順序執(zhí)行callBack隊(duì)列里面的callback.

然后接下來(lái)看看doCallbacks的執(zhí)行過(guò)程:

//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種,除了文章一開(kāi)始提到的3中外,還有一個(gè)CALLBACK_COMMIT。

CALLBACK_INPUT:輸入
CALLBACK_ANIMATION:動(dòng)畫(huà)
CALLBACK_TRAVERSAL:遍歷,執(zhí)行measure、layout、draw
CALLBACK_COMMIT:遍歷完成的提交操作,用來(lái)修正動(dòng)畫(huà)啟動(dòng)時(shí)間

然后看上面的源碼,分析一下每個(gè)callback的執(zhí)行過(guò)程:

1.callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS);得到執(zhí)行時(shí)間在當(dāng)前時(shí)間之前的所有CallBack,保存在單鏈表中。每種類型的callback按執(zhí)行時(shí)間先后順序排序分別存在一個(gè)單鏈表里面。為了保證當(dāng)前callback執(zhí)行時(shí)新post進(jìn)來(lái)的callback在下一個(gè)frame時(shí)才被執(zhí)行,這個(gè)地方extractDueCallbacksLocked會(huì)將需要執(zhí)行的callback和以后執(zhí)行的callback斷開(kāi)變成兩個(gè)鏈表,新post進(jìn)來(lái)的callback會(huì)被放到后面一個(gè)鏈表中。當(dāng)前frame只會(huì)執(zhí)行前一個(gè)鏈表中的callback,保證了在執(zhí)行callback時(shí),如果callback中Post相同類型的callback,這些新加的callback將在下一個(gè)frame啟動(dòng)后才會(huì)被執(zhí)行。

2.接下來(lái),看一大段注釋,如果類型是CALLBACK_COMMIT,并且當(dāng)前frame渲染時(shí)間超過(guò)了兩個(gè)時(shí)鐘周期,則將當(dāng)前提交時(shí)間修正為上一個(gè)垂直同步信號(hào)時(shí)間。為了保證下一個(gè)frame的提交時(shí)間和當(dāng)前frame時(shí)間相差為一且不重復(fù)。
這個(gè)地方注釋挺難看懂,實(shí)際上這個(gè)地方CALLBACK_COMMIT是為了解決ValueAnimator的一個(gè)問(wèn)題而引入的,主要是解決因?yàn)楸闅v時(shí)間過(guò)長(zhǎng)導(dǎo)致動(dòng)畫(huà)時(shí)間啟動(dòng)過(guò)長(zhǎng),時(shí)間縮短,導(dǎo)致跳幀,這里修正動(dòng)畫(huà)第一個(gè)frame開(kāi)始時(shí)間延后來(lái)改善,這時(shí)候才表示動(dòng)畫(huà)真正啟動(dòng)。為什么不直接設(shè)置當(dāng)前時(shí)間而是回溯一個(gè)時(shí)鐘周期之前的時(shí)間呢?看注釋,這里如果設(shè)置為當(dāng)前frame時(shí)間,因?yàn)閯?dòng)畫(huà)的第一個(gè)frame其實(shí)已經(jīng)繪制完成,第二個(gè)frame這時(shí)候已經(jīng)開(kāi)始了,設(shè)置為當(dāng)前時(shí)間會(huì)導(dǎo)致這兩個(gè)frame時(shí)間一樣,導(dǎo)致沖突。詳細(xì)情況請(qǐng)看官方針對(duì)這個(gè)問(wèn)題的修改。Fix animation start jank due to expensive layout operations.

如下圖所示:

image.png

3.接下來(lái)就是調(diào)用c.run(frameTimeNanos);執(zhí)行回調(diào)。
例如,你可以寫(xiě)一個(gè)自定義的FPSFrameCallback繼承自Choreographer.FrameCallback,實(shí)現(xiàn)里面的doFrame方法。

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

通過(guò)
Choreographer.getInstance().postFrameCallback(new FPSFrameCallback());
把你的回調(diào)添加到Choreographer之中,那么在下一個(gè)frame被渲染的時(shí)候就會(huì)回調(diào)你的callback,執(zhí)行你定義的doFrame操作,這時(shí)候你就可以獲取到這一幀的開(kāi)始渲染時(shí)間并做一些自己想做的事情了。

自此FrameDisplayEventReceiver注冊(cè)及其回調(diào)完成

接受并保存控制幀的信息的任務(wù)

Choreographer維護(hù)了一個(gè)隊(duì)列數(shù)組,這個(gè)數(shù)組有4個(gè)元素分別對(duì)應(yīng)于

  • INPUT:輸入事件
  • ANIMATION:動(dòng)畫(huà)
  • TRAVERSAL:窗口刷新
  • COMMIT:主要是為了處理動(dòng)畫(huà)的bug
    這四個(gè)添加的流程大致上是一致的,我們以TRAVERSAL進(jìn)行說(shuō)明,從ViewRootImp的scheduleTraversals()方法說(shuō)起:
//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();
        }
    }

會(huì)發(fā)現(xiàn)調(diào)用了mChoreographer.postCallback():

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

繼續(xù)看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);
    }

繼續(xù)看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隊(duì)列         
            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);
               //發(fā)送消息MSG_DO_SCHEDULE_CALLBACK                
               mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

在這里將callback添加到隊(duì)列,如果 不是延時(shí)直接調(diào)用cheduleFrameLocked()方法,如果是則通過(guò)handler調(diào)用,F(xiàn)rameHandler 代碼如下:

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); //【見(jiàn)小節(jié)3.5】
                break;
        }
    }
}

doScheduleCallback():

void doScheduleCallback(int callbackType) {
    synchronized (mLock) {
        if (!mFrameScheduled) {
            final long now = SystemClock.uptimeMillis();
            if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                scheduleFrameLocked(now); //【見(jiàn)小節(jié)3.6】
            }
        }
    }
}

發(fā)現(xiàn)調(diào)用的是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);
            }
        }
    }

該方法的功能:

  • 當(dāng)運(yùn)行在Looper線程,則立刻調(diào)度scheduleVsyncLocked();
  • 當(dāng)運(yùn)行在其他線程,則通過(guò)發(fā)送一個(gè)消息到Looper線程,然后再執(zhí)行scheduleVsyncLocked();
private void scheduleVsyncLocked() {
    mDisplayEventReceiver.scheduleVsync(); //【見(jiàn)小節(jié)3.8】
}

mDisplayEventReceiver對(duì)象是在Choreographer的實(shí)例化過(guò)程所創(chuàng)建的。

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

至此分析大致流程是


image.png

找到nativeScheduleVsync所對(duì)應(yīng)的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();
    ··· ···
    }
}

因?yàn)镹ativeDisplayEventReceiver 繼承DisplayEventReceiver,找到DisplayEventReceiver中scheduleVsync():

//android.view.DisplayEventReceiver

status_t DisplayEventDispatcher::scheduleVsync() {
        ··· ····
        status_t status = mReceiver.requestNextVsync();
        ··· ···
}

該方法的作用請(qǐng)求下一次Vsync信息處理。 當(dāng)Vsync信號(hào)到來(lái),由于mFrameScheduled=true,則繼續(xù)CallbackRecord.run()方法。

參考:
Android的16ms和垂直同步以及三重緩存
Choreographer原理
Android Choreographer 源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容