MessageQueue之SyncBarrier的作用

SyncBarrier作用是保證隊列中的異步類型msg的優先執行,提高特殊msg的優先級。

一、postSyncBarrier()方法

該方法是hide的,業務不能直接使用,可以通過反射方式進行調用。調用該方法,會在消息隊列中插入一個空msg。與該方法對應的public void removeSyncBarrier(int token) ;方法是刪除token對應的msg。

private int postSyncBarrier(long when) {
        // 保證多線程的正確性,加鎖
        synchronized (this) {
        	// 返回一個int型值,一個自增的成員變量
            final int token = mNextBarrierToken++;
            // 獲取一個空msg,target==null,插入到隊列中
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            // 根據消息的時間戳,插到正確的位置
            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;
        }
    }

二、MessageQueue的next()方法

設置的屏障是在出隊的時候使用的,在出隊時,判斷隊頭消息是否是空消息(說明設置了SyncBarrier),如果是,則按順序執行隊列後面的異步消息,直到SyncBarrier被remove,纔會執行隊列中的同步消息。

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // msg.target == null 說明當前隊頭是一個空消息,被設置了SyncBarrier
                if (msg != null && msg.target == null) {
                    // 找到該消息後面的異步消息(msg.isAsynchronous()方法)
                    do {
                    	// 標識前一個消息
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 還未到消息執行時間,則阻塞這段時間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        mBlocked = false;
                        // prevMsg不爲空,從隊列中刪除即將執行的msg,但是隊頭保持不變,即在下一個循環中,隊頭還是個空消息
                        // 從這裏可以看出,如果被設置了SyncBarrier,只有當SyncBarrier被移除的時候,纔會執行隊列後面的非異步消息
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                        	// 替換隊頭
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 隊列中沒有可用消息
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }

三、SyncBarrier在UI刷新中的使用

1、UI有刷新需要時,首先會執行ViewRootImpl的scheduleTraversals()方法,接收vsync信號也是通過handler機制(且msg是異步的,在Choreographer類中),所以此處設置了一個屏障,會使從當前時間點,到接收到vsync信號這段時間所有被插入到消息隊列中的同步消息失效,直到接收到vsync信號後,把屏障移除。這樣就保證了UI刷新信號的高優先級,保證了UI刷新的及時性。

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 設置屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 下一個vsync信號會回調mTraversalRunnable
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    // 該方法在接收到vsync信號時調用,執行view的measure、layout和draw
	void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            // 移除屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章