Android中老生常談的ViewRootImpl的scheduleTraversals()到底做了什么彩扔?

在Android中我們知道,Activity啟動完成之后,走完onResume方法之后胸梆,會進行window的添加蔗衡。添加過程會調(diào)用ViewRootImpl的setView()方法,setView()方法中又會調(diào)用requestLayout()方法來請求繪制布局乳绕,接著會進入到scheduleTraversals()方法,再走到performTraversals()方法逼纸,在performTraversals()方法中洋措,會進行測量、布局杰刽、繪制等三大流程菠发。

除此之外,當(dāng)我們使用ValueAnimator.start()贺嫂、View.invalidate()時滓鸠,最后也會走到ViewRootImpl的scheduleTraversals()方法,想要了解具體的第喳,可以自行查看源碼糜俗,這里就不做過多的說明了。因此曲饱,我們可以得出結(jié)論

所有UI的變化都是走到ViewRootImpl的scheduleTraversals()方法

思考這樣一個問題悠抹?當(dāng)調(diào)用ViewRootImpl的scheduleTraversals()方法之后,會立即就調(diào)用performTraversals()方法嗎扩淀?另外楔敌,我們知道現(xiàn)在Android系統(tǒng)中,當(dāng)我們接收到VSYNC信號之后才會開始繪制驻谆,那VSYNC信號又是在哪里接收的呢卵凑?帶著疑問庆聘,我們展開來說。

首先來看勺卢,ViewRootImpl的scheduleTraversals()方法伙判,代碼如下:

void scheduleTraversals() {
        //設(shè)置一個成員變量,標明是否正在執(zhí)行
        if (!mTraversalScheduled) {
            //標記位設(shè)置為true值漫,避免重復(fù)執(zhí)行
            mTraversalScheduled = true;
            //添加同步屏障澳腹,目的是屏蔽同步消息
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

可以看到,上面的代碼中杨何,調(diào)用mChoreographer.postCallback()函數(shù)時候酱塔,post的是一個mTraversalRunnable,我們來看下這個mTraversalRunnable是什么。

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

TraversalRunnable類定義在ViewRootImpl中危虱,如下:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

可以看到羊娃,該類實現(xiàn)了Runnable接口,在run()方法中埃跷,執(zhí)行了doTraversal()函數(shù)蕊玷,我們接著看下doTraversal()函數(shù)。

void doTraversal() {
        if (mTraversalScheduled) {
            //將在上面scheduleTraversals()方法中設(shè)置的標記為置為false
            mTraversalScheduled = false;
            //移除同步屏障(使用添加同步屏障的時候返回的mTraversalBarrier變量)
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //真正開始繪制流程
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

綜上分析弥雹,我們可以得出結(jié)論垃帅,在我們調(diào)用ViewRootImpl的scheduleTraversals()方法后,并不是立即就開始執(zhí)行performTraversals()的剪勿,而是需要等到使用Choreographer類的postCallback()方法post出去的Runnable回調(diào)執(zhí)行run()方法的時候贸诚,才開始真正的繪制流程,即調(diào)用performTraversals()方法厕吉。

知道了大致流程酱固,下面我們開始從源碼角度去分析。

添加同步屏障

什么是同步屏障头朱?

在Handler中运悲,消息分為三種:同步消息、異步消息项钮、同步屏障消息班眯,它們?nèi)N消息都是Message,只是屬性不同寄纵。

同步消息和異步消息的區(qū)別在于Message中的flags屬性對應(yīng)的值鳖敷。可以看一下Message的源碼:

/** If set message is asynchronous */
    static final int FLAG_ASYNCHRONOUS = 1 << 1;

在Message類中程拭,有一個setAsynchronous()方法:

public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

我們可以通過該方法定踱,將Message設(shè)置為異步消息。

除此之外恃鞋,在Handler類中還有一個成員變量mAsynchronous崖媚,該變量在Handler的某一個構(gòu)造函數(shù)中被賦值亦歉,默認是false。

 public Handler(@Nullable Callback callback, boolean async) {
        ......
        //這里可以通過Handler的構(gòu)造函數(shù)將mAsynchronous設(shè)置為true
        mAsynchronous = async;
    }

一旦我們將Hanlder的mAsynchronous設(shè)置為true畅哑,那么在Handler的enqueueMessage方法中肴楷,就會把所有到來的消息設(shè)置為異步消息

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        //這里增加了判斷,如果mAsynchronous為true荠呐,設(shè)置Message為異步消息
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

同步屏障消息

上面我們通過MessageQueue的postSyncBarrier方法添加一個同步屏障赛蔫,具體看下代碼:

public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //注意這里,在創(chuàng)建Message的時候泥张,并沒有給Message設(shè)置target(Handler)屬性
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
            //MessageQueue中存儲的Message是一個鏈式結(jié)構(gòu)
            Message prev = null;
            Message p = mMessages;
            //根據(jù)Message的時間進行排序呵恢,排序越靠前的消息,when屬性的值越小媚创,越早被執(zhí)行
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

從上述我們可以看到:

1.同步屏障消息和普通的同步異步消息的區(qū)別是渗钉,同步屏障消息沒有target屬性,因為同步屏障消息的作用是屏蔽同步消息從而保證異步消息優(yōu)先執(zhí)行的钞钙。

2.同步屏障消息和普通消息一樣鳄橘,也是根據(jù)時間來插入到消息隊列中的適當(dāng)位置,并且只會擋住它后面的同步消息的分發(fā)芒炼。

3.postSyncBarrier方法返回值是一個int類型瘫怜,該數(shù)值可以用作撤銷同步屏障。

那么同步屏障消息是如何攔住同步消息從而保證異步消息優(yōu)先處理的呢本刽?我們知道宝磨,Handler消息機制中所有的消息的獲取都是從MessageQueue的next方法中,來看下MessageQueue的next方法:


Message next() {
        ......
        for (;;) {
            .......
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // target == null條件滿足盅安,說明是同步屏障消息
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    //只取異步消息,即msg.isAsynchronous為true
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                ......
        }
    }

Choreographer的postCallback()分析

方法入?yún)?/h4>
public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

Choreographer.CALLBACK_TRAVERSAL

通過源碼可以發(fā)現(xiàn)世囊,第一個參數(shù)在Choreographer類中有五個不同的值

/**
     * Callback type: Input callback.  Runs first.
     * @hide 
     */
    public static final int CALLBACK_INPUT = 0;

    /**
     * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
     * @hide
     */
    @TestApi
    public static final int CALLBACK_ANIMATION = 1;

    /**
     * Callback type: Animation callback to handle inset updates. This is separate from
     * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
     * that contains all the combined updated insets.
     * <p>
     * Both input and animation may change insets, so we need to run this after these callbacks, but
     * before traversals.
     * <p>
     * Runs before traversals.
     * @hide
     */
    public static final int CALLBACK_INSETS_ANIMATION = 2;

    /**
     * Callback type: Traversal callback.  Handles layout and draw.  Runs
     * after all other asynchronous messages have been handled.
     * @hide
     */
    public static final int CALLBACK_TRAVERSAL = 3;

    /**
     * Callback type: Commit callback.  Handles post-draw operations for the frame.
     * Runs after traversal completes.  The {@link #getFrameTime() frame time} reported
     * during this callback may be updated to reflect delays that occurred while
     * traversals were in progress in case heavy layout operations caused some frames
     * to be skipped.  The frame time reported during this callback provides a better
     * estimate of the start time of the frame in which animations (and other updates
     * to the view hierarchy state) actually took effect.
     * @hide
     */
    public static final int CALLBACK_COMMIT = 4;

上述注釋解釋的很清楚:

事件值 解釋說明
CALLBACK_INPUT 輸入事件别瞭,第一個執(zhí)行
CALLBACK_ANIMATION 動畫,在CALLBACK_INSETS_ANIMATION前執(zhí)行
CALLBACK_INSETS_ANIMATION 輸入和動畫之后株憾,TRAVERSAL之前
CALLBACK_TRAVERSAL 處理布局和繪制蝙寨,運行在其他異步消息之后
CALLBACK_COMMIT 提交,最后執(zhí)行


第二個參數(shù)需要的是一個Runnable

第三個參數(shù)為Object token

我們在ViewRootImpl中調(diào)用scheduleTraversals()方法中:

mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

第一個參數(shù)傳入的是CALLBACK_TRAVERSAL(布局和繪制)嗤瞎。

第二個參數(shù)傳入的是mTraversalRunnable墙歪,上面已經(jīng)說過,就是一個實現(xiàn)了Runnable接口的TraversalRunnable類贝奇。

第三個參數(shù)傳入的是null虹菲。

接著往下分析,可以看到在Choreographer類中又調(diào)用了

 //注意這里傳入的delayMillis是0
 postCallbackDelayed(callbackType, action, token, 0);

接著看該方法:

public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
        //判斷傳入的Runnable是否為null掉瞳,如果為null毕源,拋異常
        if (action == null) {
            throw new IllegalArgumentException("action must not be null");
        }
        //判斷傳入的callbackType是否正常(是否滿足該類中定義的五個值)
        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
            throw new IllegalArgumentException("callbackType is invalid");
        }
        //接著往下調(diào)用
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

通過一些判斷之后浪漠,接著往下走:

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) {
            //由于傳入的delayMillis是0,所以dueTime == now
            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);
            }
        }
    }

上述代碼中霎褐,mCallbackQueues是Choreographer類中的一個數(shù)組址愿,初始化是在Choreographer類的構(gòu)造方法中:

mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }

mCallbackQueues的長度是5,并且為數(shù)組中的每一個元素都新建了一個CallbackQueue冻璃∠煳剑看下CallbackQueue,CallbackQueue是Choreographer類的一個內(nèi)部類:

private final class CallbackQueue {
        private CallbackRecord mHead;

        public boolean hasDueCallbacksLocked(long now) {
            return mHead != null && mHead.dueTime <= now;
        }

        public CallbackRecord extractDueCallbacksLocked(long now) {
            ......
        }

        @UnsupportedAppUsage
        public void addCallbackLocked(long dueTime, Object action, Object token) {
            ......
        }

        public void removeCallbacksLocked(Object action, Object token) {
            ......
        }
    }

可以看到省艳,在CallbackQueue中存儲的是CallbackRecord娘纷,看下CallbackRecord類:

private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

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

CallbackRecord類中有一個成員變量是next,指向的是下一個CallbackRecord拍埠,因此在CallbackQueue中存儲的是一個鏈式結(jié)構(gòu)的CallbackRecord失驶。注意這里的run()方法,下面會用到枣购。

回到postCallbackDelayedInternal()方法中繼續(xù)看嬉探,我們現(xiàn)在傳入的delayMillis是0,因此dueTime == now,所以會執(zhí)行scheduleFrameLocked(now)方法棉圈。那如果走到else呢涩堤?答案是依然會執(zhí)行scheduleFrameLocked(now)方法。

簡單看下:

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);

如果delayMillis不是0分瘾,那么會使用mHandler發(fā)送一個下次胎围,該Message的what是 MSG_DO_SCHEDULE_CALLBACK 。mHandler指向的是Choreographer類中的FrameHandler德召,我們看下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);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

當(dāng) msg.what = MSG_DO_SCHEDULE_CALLBACK 的時候白魂,執(zhí)行的是doScheduleCallback(msg.arg1),看下該方法:

void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
                final long now = SystemClock.uptimeMillis();
                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                    scheduleFrameLocked(now);
                }
            }
        }
    }

可以看到這里依然會走到scheduleFrameLocked(now)方法中上岗。至于判斷條件福荸,有興趣的可以自己去分析下。

接下來來看一下scheduleFrameLocked(now)方法:

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

可以看到肴掷,方法進來之后敬锐,先判斷了mFrameScheduled是否為true,此時不是true呆瞻,因此進入方法體中台夺,并且將mFrameScheduled設(shè)置為true。緊接著判斷USE_VSYNC是否為true痴脾,這個參數(shù)指的是是否開啟了VSYNC颤介,我們通過查資料可以知道在Android 4.1 之后默認開始了VSYNC,因此如果當(dāng)前Android 系統(tǒng)是4.1以后,那么會走if里面的邏輯买窟,反之則會調(diào)用else里面的邏輯丰泊。這里稍微看下4.1之前的邏輯,即不使用VSYNC信號始绍,則會直接發(fā)送一個 what = MSG_DO_FRAME的Message瞳购,通過上面FrameHandler的代碼可以知道,會調(diào)用doFrame方法亏推。doFrame方法我們下面再分析学赛。這里只要知道,如果沒有使用VSYNC信號吞杭,則會直接調(diào)用doFrame方法即可盏浇。

如果使用VSYNC信號,即進入if中芽狗,這里會判斷绢掰,isRunningOnLooperThreadLocked(),該方法內(nèi)部根據(jù)Looper判斷會否在原線程。

 private boolean isRunningOnLooperThreadLocked() {
        return Looper.myLooper() == mLooper;
    }

如果是在原線程童擎,則調(diào)用scheduleVsyncLocked()方法滴劲,否則發(fā)送一個 what = MSG_DO_SCHEDULE_VSYNC 的Message,繼續(xù)看FrameHandler的代碼發(fā)現(xiàn)顾复,如果what = MSG_DO_SCHEDULE_VSYNC班挖,會調(diào)用doScheduleVsync()方法

void doScheduleVsync() {
        synchronized (mLock) {
            if (mFrameScheduled) {
                scheduleVsyncLocked();
            }
        }
    }

在該方法中,因為在scheduleFrameLocked()方法中我們已經(jīng)將mFrameScheduled設(shè)置為true芯砸,因此會執(zhí)行scheduleVsyncLocked()方法萧芙。

到這里我們已經(jīng)可以看到FrameHandler的作用了:發(fā)送異步消息。因為我們之前已經(jīng)設(shè)置了同步屏障假丧,因此該異步消息會被執(zhí)行双揪。同時,如果任務(wù)是延遲的任務(wù)包帚,發(fā)送延遲消息盟榴。如果不在原線程,通過mHandlder發(fā)送消息到原線程.如果沒有開啟VSYNC的直接調(diào)用doFramen方法婴噩。

接著來看下scheduleVsyncLocked()方法。

private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

mDisplayEventReceiver是FrameDisplayEventReceiver的實例羽德,我們看下FrameDisplayEventReceiver几莽。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

      
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            ......
        }

        @Override
        public void run() {
            ......
        }
    }

可以看到,F(xiàn)rameDisplayEventReceiver繼承自DisplayEventReceiver宅静,并實現(xiàn)了Runnable接口章蚣。實現(xiàn)了兩個方法,onVsync()和run(),其中onVsync方法來自DisplayEventReceiver,run()方法來自Runnable接口纤垂。mDisplayEventReceiver是在Choreographer的構(gòu)造方法中創(chuàng)建出來的矾策,找到對應(yīng)的代碼:

 private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        ......
    }

可以看到,如果沒有使用VSYNC峭沦,則mDisplayEventReceiver為null贾虽,反之則創(chuàng)建。在創(chuàng)建的時候吼鱼,傳入了當(dāng)前的looper蓬豁。在該類中沒有找到scheduleVsync()方法,那我們就去父類中去找菇肃,先看下父類的構(gòu)造方法

public DisplayEventReceiver(Looper looper, int vsyncSource) {
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }

        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
                vsyncSource);

        mCloseGuard.open("dispose");
    }

可以看到地粪,在父類的構(gòu)造方法中,先判斷了looper是否為null琐谤。然后從looper中獲取到MessageQueue賦值給mMessageQueue蟆技,然后調(diào)用了一個native的方法,并將返回值賦值給了mReceiverPtr斗忌。我們看下父類的scheduleVsync()方法质礼。

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

在該方法中,先判斷了mReceiverPtr是否為0飞蹂。這里應(yīng)該就是在構(gòu)造方法中几苍,如果調(diào)用nativeInit方法成功的話,值就不是0陈哑。因此如果mReceiverPtr不是0妻坝,會調(diào)用nativeScheduleVsync(mReceiverPtr)。該方法也是一個native方法惊窖,這里就不往下看了刽宪,我們只需要知道VSYNC信號的接收回調(diào)是onVsync()方法。因此我們繼續(xù)看onVsync方法界酒。在DisplayEventReceiver中圣拄,onVsync是一個空實現(xiàn),因此我們回到FrameDisplayEventReceiver類的onVsync方法

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

上面的直接省略毁欣,我們看下通過mHandler發(fā)送消息庇谆,可以看到,這里創(chuàng)建Message消息的時候凭疮,傳入的是this饭耳,因為FrameDisplayEventReceiver實現(xiàn)了Runnable接口,因此在handler處理消息的時候回調(diào)run方法执解,在run方法中寞肖,調(diào)用了doFramen方法。另外,這里調(diào)用了msg.setAsynchronous(true)新蟆,通過handler發(fā)送到MessageQueue中的是一個異步消息觅赊,我們前面已經(jīng)post了一個同步屏障,因此該異步消息會被調(diào)用琼稻。
注意吮螺,在上面我們提到了,在Android 4.1之前欣簇,也會走到doFramen方法中去规脸。
下面來看下doFrame方法

void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
        ......
        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中依次執(zhí)行了doCallbacks方法熊咽,傳入的類型是我們上面提到的Choreographer類中定義好的五種類型莫鸭。看下doCallbacks方法:

 void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                            now / TimeUtils.NANOS_PER_MS);
                    if (callbacks == null) {
                        return;
                    }
                ......
                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);
            }
    }

可以看到横殴,首先根據(jù)指定的任務(wù)類型被因,從mCallbackQueues中查找需要執(zhí)行的CallbackRecord,如果是null衫仑,直接return了梨与。緊接著,遍歷執(zhí)行CallbackRecord中的所有任務(wù)文狱,回調(diào)CallbackRecord的run 方法粥鞋。

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

因為我們在上面往CallbackRecord中add任務(wù)的時候,傳入的token是null瞄崇,所以這里直接走到了action.run()方法呻粹,而action是前面提到的TraversalRunnable,所以直接調(diào)用了TraversalRunnable類的run方法苏研,然后調(diào)用了doTraversal()等浊,在doTraversal()方法中,開始真正的繪制流程摹蘑〕镅啵看下doTraversal方法:

void doTraversal() {
        if (mTraversalScheduled) {
            //標記位設(shè)為false
            mTraversalScheduled = false;
            //從MessageQueue中移除同步屏障消息
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //真正開始繪制流程
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

在調(diào)用performTraversals之前,調(diào)用了mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier),在這里就把之前我們添加的同步屏障從MessageQueue中移除衅鹿,從而使MesssageQueue中next()方法可以正常取到同步消息撒踪。

那么什么時候token == FRAME_CALLBACK_TOKEN成立呢?我們在Choregrapher類中查找FRAME_CALLBACK_TOKEN發(fā)現(xiàn)大渤,在調(diào)用postFrameCallback的時候制妄,會調(diào)用postFrameCallbackDelayed

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }

        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

在這里傳入了FRAME_CALLBACK_TOKEN。那么該方法的執(zhí)行流程呢兼犯?可以看到,在這個方法中也是調(diào)用到了postCallbackDelayedInternal方法,只不過傳入了token參數(shù)切黔。并且這里傳入的是action是FrameCallback砸脊,F(xiàn)rameCallback中只有一個doFrame方法。postFrameCallback通常情況下用來計算丟幀情況纬霞。
使用一張圖來梳理一下在整個Choreographer中的流程

在此感謝下面參考文章的作者凌埂,在沒有看到該作者的文章之前,也看過這個繪制流程的源碼诗芜,但總是看不進去瞳抓,看一點忘一點。后來看到這篇文章之后伏恐,結(jié)合文章中所介紹的內(nèi)容再去看源碼孩哑,靜下心來,慢慢的就看明白了翠桦,特此感謝横蜒。其實我們學(xué)習(xí)就是這樣了,找到一個自己喜歡的學(xué)習(xí)方法销凑,學(xué)習(xí)就會變得有樂趣丛晌。想要成長起來,源碼是繞不開的斗幼,通過看源碼澎蛛、理解源碼能夠使我們加深對于整個Android系統(tǒng)的理解,從而讓自己更上一層樓蜕窿。

參考文章https://juejin.cn/post/6863756420380196877

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谋逻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子渠羞,更是在濱河造成了極大的恐慌斤贰,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件次询,死亡現(xiàn)場離奇詭異荧恍,居然都是意外死亡,警方通過查閱死者的電腦和手機屯吊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門送巡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盒卸,你說我怎么就攤上這事骗爆。” “怎么了蔽介?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵摘投,是天一觀的道長煮寡。 經(jīng)常有香客問我,道長犀呼,這世上最難降的妖魔是什么幸撕? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮外臂,結(jié)果婚禮上坐儿,老公的妹妹穿的比我還像新娘。我一直安慰自己宋光,他們只是感情好貌矿,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罪佳,像睡著了一般逛漫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菇民,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天尽楔,我揣著相機與錄音,去河邊找鬼第练。 笑死阔馋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娇掏。 我是一名探鬼主播呕寝,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼婴梧!你這毒婦竟也來了下梢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤塞蹭,失蹤者是張志新(化名)和其女友劉穎孽江,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體番电,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡岗屏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了漱办。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片这刷。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖娩井,靈堂內(nèi)的尸體忽然破棺而出暇屋,到底是詐尸還是另有隱情,我是刑警寧澤洞辣,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布咐刨,位于F島的核電站昙衅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏定鸟。R本人自食惡果不足惜绒尊,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仔粥。 院中可真熱鬧,春花似錦蟹但、人聲如沸躯泰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽麦向。三九已至,卻和暖如春客叉,著一層夾襖步出監(jiān)牢的瞬間诵竭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工兼搏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卵慰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓佛呻,卻偏偏與公主長得像裳朋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吓著,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1鲤嫡、基本概念 Android屏幕的刷新包括3個重要的部分:CPU、GPU绑莺、屏幕暖眼。 1、CPU:負責(zé)View的測量纺裁、...
    code希必地閱讀 699評論 0 0
  • @[TOC] 閱讀本篇可能需要的預(yù)備知識《View的工作原理》、《Handler:Android消息機制》浪南、《Wi...
    胡飛洋閱讀 2,864評論 1 16
  • 1笼才、概述 不論電腦,電視络凿,手機骡送,我們看到的畫面都是由一幀幀的畫面組成的昂羡。FPS是圖像領(lǐng)域中的定義,是指畫面每秒傳輸...
    高丕基閱讀 12,016評論 6 34
  • 久違的晴天摔踱,家長會虐先。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了派敷。班主任說已經(jīng)安排了三個家長分享經(jīng)驗蛹批。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,494評論 16 22
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友篮愉。感恩相遇腐芍!感恩不離不棄。 中午開了第一次的黨會试躏,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,551評論 0 11