Choreographer源碼理解

Choreographer的作用

1、負(fù)責(zé)接收和處理 App 的各種更新消息和回調(diào),等到 Vsync 到來(lái)的時(shí)候統(tǒng)一處理。比如集中處理 Input(主要是 Input 事件的處理) 、Animation(動(dòng)畫相關(guān))、Traversal(包括 measure、layout、draw 等操作) ,判斷卡頓掉幀情況,記錄 CallBack 耗時(shí)等

2、負(fù)責(zé)請(qǐng)求和接收 Vsync 信號(hào)。接收 Vsync 事件回調(diào)(通過(guò) FrameDisplayEventReceiver.onVsync );請(qǐng)求 Vsync(FrameDisplayEventReceiver.scheduleVsync) .

Choreographer初始化
private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;//1
        mHandler = new FrameHandler(looper);//2
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;//3
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];//4
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        // b/68769804: For low FPS experiments.
        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }

首先需要一個(gè)Looper,然后通過(guò)Looper來(lái)創(chuàng)建一個(gè)Handler對(duì)象。載創(chuàng)建一個(gè)mDisplayEventReceiver對(duì)象,主要是處理Vsync信號(hào)的回調(diào)和請(qǐng)求。最后初始化一個(gè)回調(diào)隊(duì)列。

FrameHandler
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);//1
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();//2
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);//3
                    break;
            }
        }
    }

1表示開(kāi)始下一幀的渲染操作

2表示請(qǐng)求Vsync

3表示處理callback

FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
    
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, 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);
        }
    }

該類繼承DisplayEventReceiver,實(shí)現(xiàn)其兩個(gè)方法為onVsync()和run(),onVsync()主要是處理Vsync信號(hào)的回調(diào),然后將消息設(shè)置成異步,通過(guò)我們的FrameHandler發(fā)送出去。run()則是調(diào)用doFrame(),表示我們接收到Vsync信號(hào)之后,開(kāi)始下一幀的渲染。

onVsync()方法中只是使用mHandler發(fā)送消息到MessageQueue中,不一定是立刻執(zhí)行,如果MessageQueue中前面有較為耗時(shí)的操作,那么就要等完成,才會(huì)執(zhí)行本次的doFrame()。

前面說(shuō)了,F(xiàn)rameDisplayEventReceiver是請(qǐng)求和回調(diào),那請(qǐng)求時(shí)在哪里了?

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

真正的請(qǐng)求是在DisplayEventReceiver類中的scheduleVsync(),里面調(diào)用的是一個(gè)native方法來(lái)進(jìn)行請(qǐng)求Vsync信號(hào)。DisplayEventReceiver 初始化完成后,Choreographer 與 SurfaceFlinger 的 Vsync 信號(hào)傳遞通道就建立好了,后續(xù)有 Vsync 信號(hào)到來(lái)時(shí),就可以沿著這條通道,一路回調(diào)到 Java 層的 Choreographer.FrameDisplayEventReceiver.onVsync 方法。

callback類型
//輸入事件,首先執(zhí)行
    public static final int CALLBACK_INPUT = 0;
    //動(dòng)畫,第二執(zhí)行
    public static final int CALLBACK_ANIMATION = 1;
    //繪制,第三執(zhí)行
    public static final int CALLBACK_TRAVERSAL = 2;
    //提交,最后執(zhí)行,
    public static final int CALLBACK_COMMIT = 3;

callback類型四種,其執(zhí)行的順序?yàn)?-》1-》2-》3

執(zhí)行流程

那什么時(shí)候會(huì)用到Choreographer,通過(guò)調(diào)用invalidate()或者requestLayout()方法的時(shí)候,也就是我們需要UI繪制的時(shí)候,需要用到Choreographer。

上面的兩個(gè)方法最終都會(huì)走到scheduleTraversals()。

scheduleTraversals()
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

首先會(huì)設(shè)置一個(gè)同步屏障,然后調(diào)用postCallback()方法.postCallback()方法中繼續(xù)調(diào)用postCallbackDelayed(),最終會(huì)調(diào)用postCallbackDelayedInternal()方法。

postCallbackDelayedInternal()
private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long 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);
            }
        }
    }

如果dueTime<=當(dāng)前時(shí)間,則會(huì)去請(qǐng)求Vsync信號(hào),否則通過(guò)handler發(fā)送一條異步消息到隊(duì)列中。

private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                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);
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

1、如果系統(tǒng)未開(kāi)啟 VSYNC 機(jī)制,此時(shí)直接發(fā)送 MSG_DO_FRAME 消息到 FrameHandler,此時(shí)直接執(zhí)行 doFrame 方法。

2、Android 4.1 之后系統(tǒng)默認(rèn)開(kāi)啟 VSYNC,在 Choreographer 的構(gòu)造方法會(huì)創(chuàng)建一個(gè) FrameDisplayEventReceiver,scheduleVsyncLocked 方法將會(huì)通過(guò)它申請(qǐng) VSYNC 信號(hào)。

3、isRunningOnLooperThreadLocked 方法,其內(nèi)部根據(jù) Looper 判斷是否在原線程,否則發(fā)送消息到 FrameHandler。最終還是會(huì)調(diào)用 scheduleVsyncLocked 方法申請(qǐng) VSYNC 信號(hào)。

請(qǐng)求Vsync信號(hào)成功之后,就會(huì)走onVsync()回調(diào)。onVsync()中,將接收器本身作為runnable傳入異步消息msg,并使用mHandler發(fā)送msg,最終執(zhí)行的就是doFrame()方法。

doFrame()
void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            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;
            }

            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    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);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

在doFrame方法中,主要做了以下的事:

1、計(jì)算掉幀邏輯

Choreographer.doFrame 的掉幀檢測(cè)比較簡(jiǎn)單,Vsync 信號(hào)到來(lái)的時(shí)候會(huì)標(biāo)記一個(gè) start_time ,執(zhí)行 doFrame 的時(shí)候標(biāo)記一個(gè) end_time ,這兩個(gè)時(shí)間差就是 Vsync處理時(shí)延,也就是掉幀。
這種方法計(jì)算的掉幀,是前一幀的掉幀情況,而不是這一幀的掉幀情況,這個(gè)計(jì)算方法是有缺陷的,會(huì)導(dǎo)致有的掉幀沒(méi)有被計(jì)算到

2、記錄幀繪制信息

Choreographer 中 FrameInfo 來(lái)負(fù)責(zé)記錄幀的繪制信息,doFrame 執(zhí)行的時(shí)候,會(huì)把每一個(gè)關(guān)鍵節(jié)點(diǎn)的繪制時(shí)間記錄下來(lái),我們使用 dumpsys gfxinfo 就可以看到。

3、執(zhí)行 CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT

最后執(zhí)行的是c.run(frameTimeNanos);

public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }

最后調(diào)用的是run();

現(xiàn)在我們?cè)賮?lái)看scheduleTraversals()

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            
        }
    }

((Runnable)action).run()其實(shí)執(zhí)行的是mTraversalRunnable。

mTraversalRunnable()
final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();//1
        }
    }

從1可以知道,熟悉view的繪制流程的就知道在performTraversals()中會(huì)去執(zhí)行Measure(),Layout(),Draw(),就這樣就形成了一個(gè)閉環(huán)。

vsync.png
小結(jié)

1、Choreographer 是線程單例的,而且必須要和一個(gè) Looper 綁定,因?yàn)槠鋬?nèi)部有一個(gè) Handler 需要和 Looper 綁定,一般是 App 主線程的 Looper 綁定。

2、DisplayEventReceiver 是一個(gè) abstract class,其 JNI 的代碼部分會(huì)創(chuàng)建一個(gè)IDisplayEventConnection 的 Vsync 監(jiān)聽(tīng)者對(duì)象。這樣,來(lái)自 AppEventThread 的 VSYNC 中斷信號(hào)就可以傳遞給 Choreographer 對(duì)象了。當(dāng) Vsync 信號(hào)到來(lái)時(shí),DisplayEventReceiver 的 onVsync 函數(shù)將被調(diào)用。

3、DisplayEventReceiver 還有一個(gè) scheduleVsync 函數(shù)。當(dāng)應(yīng)用需要繪制UI時(shí),將首先申請(qǐng)一次 Vsync 中斷,然后再在中斷處理的 onVsync 函數(shù)去進(jìn)行繪制。

4、Choreographer 定義了一個(gè) FrameCallback interface,每當(dāng) Vsync 到來(lái)時(shí),其 doFrame 函數(shù)將被調(diào)用。這個(gè)接口對(duì) Android Animation 的實(shí)現(xiàn)起了很大的幫助作用。以前都是自己控制時(shí)間,現(xiàn)在終于有了固定的時(shí)間中斷。

5、Choreographer 的主要功能是,當(dāng)收到 Vsync 信號(hào)時(shí),去調(diào)用使用者通過(guò) postCallback 設(shè)置的回調(diào)函數(shù)。目前一共定義了四種類型的回調(diào),它們分別是:

CALLBACK_INPUT : 處理輸入事件處理有關(guān)
CALLBACK_ANIMATION : 處理 Animation 的處理有關(guān)
CALLBACK_TRAVERSAL : 處理和 UI 等控件繪制有關(guān)
CALLBACK_COMMIT : 處理 Commit 相關(guān)回調(diào)

6、CALLBACK_INPUT 、CALLBACK_ANIMATION 會(huì)修改 view 的屬性,所以要先于 CALLBACK_TRAVERSAL 執(zhí)行

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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