【Android】消息隊列之異步消息

異步消息可以理解爲是用來提升消息的重要性,在有異步消息時,會優先處理距當前時間最近的異步消息,同步消息都不會被處理,繪製界面就是一個異步消息;

同步消息:沒有設置爲異步的消息都是同步消息;

Message提供setAsynchronous用來設置異步消息;

一、異步消息的使用流程

1.使用MessageQueue的postSyncBarrier() 來加入同步屏障;同步屏障其實就是一個handler爲空的消息,它也存放在消息隊列中;

2.往消息隊列中加入異步消息,跟平時創建消息沒有什麼不同,只是額外調用setAsynchronous(true)來設置異步消息;

3.線程執行過程中,是怎麼取消息的,異步消息爲什麼會優先於同步消息;【介紹下原理,可以不關注】

Looper的loop會一直循環取消息進行處理,我們主要關注的是MessageQueue的next方法,它在消息隊列頭部是屏障消息的時候,會在它後面找最近的異步消息,如果時間合適的話就返回這個消息,如果時間不ok就休眠等待;

這裏有個隱含意思:同步屏障消息在隊列頭部纔有效,才需要處理異步消息,如果同步屏障消息不在頭部的話,不影響它前面的同步消息的處理;

可以看下next關鍵代碼:

Message msg = mMessages;
//target爲空表示是同步屏障消息
if (msg != null && msg.target == null) {
    // Stalled by a barrier.  Find the next asynchronous message in the queue.
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}

4.異步消息執行時,使用MessageQueue的removeSyncBarrier來刪除同步屏障;

二、繪製消息的處理過程

1.加入同步屏障;

我們都知道繪製消息都是在ViewRootImpl中處理的,這裏我們只看scheduleTraversals方法;

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //添加同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //設置繪製處理函數
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

2.設置異步消息;

硬件底層嚮應用層發出垂直同步信號後,會回調Choreographer內部類FrameDisplayEventReceiver的onVsync方法,其內部主要是向消息隊列中放入異步消息;異步消息執行時會調用FrameDisplayEventReceiver的run方法來處理;

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

        // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
        // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
        // for the internal display implicitly.
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            // Post the vsync event to the Handler.
            // The idea is to prevent incoming vsync events from completely starving
            // the message queue.  If there are no messages in the queue with timestamps
            // earlier than the frame time, then the vsync event will be processed immediately.
            // Otherwise, messages that predate the vsync event will be handled first.
            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

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

3.刪除同步屏障;

第2步中的doFrame會執行Choreographer裏設置的所有回調消息,包括第1步中設置的Choreographer.CALLBACK_TRAVERSAL消息;

這會執行到ViewRootImpl的TraversalRunnable,TraversalRunnable主要就是調用doTraversal方法;

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //刪除同步屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章