復(fù)盤 Handler 中各式 Message 的使用和原理

我們會(huì)經(jīng)常使用 Handlersendpost 一個(gè)延時(shí)铆隘、非延時(shí)插隊(duì)執(zhí)行的 Message鸠真,但對(duì)于這個(gè) Message 到底什么時(shí)候執(zhí)行以及為什么是這樣馍刮,鮮少細(xì)究過徊件。本文將一 一盤點(diǎn)梭依,并起底個(gè)中原理!

同時(shí)針對(duì)大家疏于了解的異步執(zhí)行 Message 和空閑執(zhí)行 IdleHandler惕虑,進(jìn)行演示和原理普及坟冲。篇幅較大,搭配收藏食用更佳~

目錄一覽:

  • 非延時(shí)執(zhí)行 Message
  • 延時(shí)執(zhí)行 Message
  • 插隊(duì)執(zhí)行 Message
  • 異步執(zhí)行 Message
  • 空閑執(zhí)行 “Message”
  • 名詞回顧

非延時(shí)執(zhí)行 Message

先在主線程創(chuàng)建一個(gè) Handler 并復(fù)寫 Callback 處理溃蔫。

    private val mainHandler = Handler(Looper.getMainLooper()) { msg ->
        Log.d(
            "MainActivity",
            "Main thread message occurred & what:${msg.what}"
        )
        true
    }

不斷地發(fā)送期望即刻執(zhí)行的 Message 和 Runnable 給主線程的 Handler健提。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        testSendNoDelayedMessages()
    }

    private fun testSendNoDelayedMessages() {
        Log.d("MainActivity","testSendNoDelayedMessages() start")
        testSendMessages()
        testPostRunnable()
        Log.d("MainActivity","testSendNoDelayedMessages() end ")
    }

    private fun testSendMessages() {
        Log.d("MainActivity","startSendMessage() start")
        for (i in 1..10) {
            sendMessageRightNow(mainHandler, i)
        }
        Log.d("MainActivity","startSendMessage() end ")
    }

    private fun testPostRunnable() {
        Log.d("MainActivity","testPostRunnable() start")
        for (i in 11..20) {
            mainHandler.post { Log.d("MainActivity", "testPostRunnable() run & i:${i}") }
        }
        Log.d("MainActivity","testPostRunnable() end ")
    }

什么時(shí)候執(zhí)行?

公布下日志前伟叛,大家可以猜測下運(yùn)行的結(jié)果私痹,Message 或 Runnable 在 send 或 post 之后會(huì)否立即執(zhí)行。不是的話统刮,什么時(shí)候執(zhí)行紊遵?

 D MainActivity: testSendNoDelayedMessages() start
 D MainActivity: startSendMessage() start
 D MainActivity: startSendMessage() end
 D MainActivity: testPostRunnable() start
 D MainActivity: testPostRunnable() end
 D MainActivity: testSendNoDelayedMessages() end
 D MainActivity: Main thread message occurred & what:1
 ...
 D MainActivity: Main thread message occurred & what:10
 D MainActivity: testPostRunnable() run & i:11
 ...
 D MainActivity: testPostRunnable() run & i:20

答案可能跟預(yù)想的略有出入,但一細(xì)想好像又是合理的:發(fā)送完的 Message 或 Runnable 不會(huì)立即執(zhí)行侥蒙,MessageQueue 的喚醒和回調(diào)需要等主線程的其他工作完成之后才能執(zhí)行暗膜。

為什么?

非延時(shí)的 sendMessage()post() 最終仍然是調(diào)用 sendMessageAtTime() 將 Message 放入了 MessageQueue鞭衩。只不過它的期待執(zhí)行時(shí)間 when 變成了 SystemClock.uptimeMillis()学搜,即調(diào)用的時(shí)刻。

// Handler.java
    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); // when 等于當(dāng)前時(shí)刻
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        return enqueueMessage(queue, msg, uptimeMillis);
    }

這些 Message 會(huì)按照 when 的先后排隊(duì)進(jìn)入 MessageQueue 中论衍,當(dāng) Message 滿足了條件會(huì)立即調(diào)用 wake瑞佩,反之只是插入隊(duì)列而已。所以坯台,上述的 send 或 post 循環(huán)炬丸,會(huì)按照調(diào)用的先后挨個(gè)進(jìn)入隊(duì)列,第一個(gè) Message 會(huì)觸發(fā) wake蜒蕾。

// MessageQueue.java
    boolean enqueueMessage(Message msg, long when) {
        ...
        // 鑒于多線程往 Handler 里發(fā)送 Message 的情況
        // 在向隊(duì)列插入 Message 前需要上鎖
        synchronized (this) {
            ...
            msg.markInUse(); // Message 標(biāo)記正在使用
            msg.when = when; // 更新 when 屬性
            Message p = mMessages; // 拿到隊(duì)列的 Head 
            boolean needWake;
            // 如果隊(duì)列為空
            // 或者 Message 需要插隊(duì)(sendMessageAtFrontOfQueue)
            // 又或者 Message 執(zhí)行時(shí)刻比 Head 的更早
            // 該 Message 插入到隊(duì)首
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                // 線程是否因?yàn)闆]有可執(zhí)行的 Message 正在 block 或 wait
                // 是的話稠炬,喚醒
                needWake = mBlocked;
            } else {
                // 如果隊(duì)列已有 Message焕阿,Message 優(yōu)先級(jí)又不高,同時(shí)執(zhí)行時(shí)刻并不早于隊(duì)首的 Message
                // 如果線程正在 block 或 wait酸纲,或建立了同步屏障(target 為空)捣鲸,并且 Message 是異步的,則喚醒
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                // 遍歷隊(duì)列闽坡,找到 Message 目標(biāo)插入位置
                for (;;) {
                    prev = p;
                    p = p.next;
                    // 如果已經(jīng)遍歷到隊(duì)尾了,或 Message 的時(shí)刻比當(dāng)前 Message 要早
                    // 找到位置了愁溜,退出遍歷
                    if (p == null || when < p.when) {
                        break;
                    }
                    
                    // 如果前面決定需要喚醒疾嗅,但隊(duì)列已有執(zhí)行時(shí)刻更早的異步 Message 的話,先不喚醒
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 將 Message 插入隊(duì)列的目標(biāo)位置
                msg.next = p;
                prev.next = msg;
            }

            // 需要喚醒的話冕象,喚醒 Native 側(cè)的 MessageQueue
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

總結(jié)來講:

  • 第一次 send 的 Message 在 enqueue 進(jìn) MessageQueue 的隊(duì)首后代承,通知 Native 側(cè) wake
  • 后續(xù)發(fā)送的其他 Message 或 Runnable 挨個(gè) enqueue 進(jìn)隊(duì)列
  • 接著執(zhí)行主線程的其他 Message,比如日志的打印
  • 空閑后 wake 完畢并在 next() 的下一次循環(huán)里將隊(duì)首 Message 移除和返回給 Looper 去回調(diào)和執(zhí)行
  • 之后 loop() 開始讀取 MessageQueue 當(dāng)前隊(duì)首 Message 的下一次循環(huán)渐扮,當(dāng)前時(shí)刻必然晚于 send 時(shí)候設(shè)置的when论悴,所以隊(duì)列里的 Message 挨個(gè)出隊(duì)和回調(diào)

結(jié)論

非延時(shí) Message 并非立即執(zhí)行,只是放入 MessageQueue 等待調(diào)度而已墓律,執(zhí)行時(shí)刻不確定膀估。

MessageQueue 會(huì)記錄請(qǐng)求的時(shí)刻,按照時(shí)刻的先后順序進(jìn)行排隊(duì)耻讽。如果 MessageQueue 中積攢了很多 Message察纯,或主線程被占用的話,Message 的執(zhí)行會(huì)明顯晚于請(qǐng)求的時(shí)刻针肥。

比如在 onCreate() 里發(fā)送 message 的話饼记,你會(huì)發(fā)現(xiàn)當(dāng) onResume() 執(zhí)行完才會(huì)回調(diào)你的 Message。原因在于 onCreate() 等操作也是由 Message 觸發(fā)慰枕,其同步處理完 onResume() 之后具则,才有機(jī)會(huì)進(jìn)入下一次循環(huán)去讀取你的 Message。

延時(shí)執(zhí)行 Message

延時(shí)執(zhí)行的 Message 使用更為常見具帮,那它又是何時(shí)執(zhí)行呢博肋?

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        testSendDelayedMessages()
    }

    private fun testSendDelayedMessages() {
        Log.d("MainActivity","testSendDelayedMessages() start")
        // 發(fā)送 Delay 2500 ms 的 Message
        sendDelayedMessage(mainHandler, 1)
        Log.d("MainActivity","testSendDelayedMessages() end ")
    }

28:58.186 發(fā)送 Message,29:00.690 Message 執(zhí)行匕坯,時(shí)間差為 2504ms束昵,并非準(zhǔn)確的 2500ms。

09-22 22:28:57.964 24980 24980 D MainActivity: onCreate()
09-22 22:28:58.186 24980 24980 D MainActivity: testSendDelayedMessages() start
// 發(fā)送 Message
09-22 22:28:58.186 24980 24980 D MainActivity: testSendDelayedMessages() end
// Message 執(zhí)行
09-22 22:29:00.690 24980 24980 D MainActivity: Main thread message occurred & what:1

如果連續(xù)發(fā)送 10 個(gè)均延時(shí) 2500ms 的 Message 會(huì)怎么樣葛峻?

    private fun testSendDelayedMessages() {
        Log.d("MainActivity","testSendDelayedMessages() start")
        // 連續(xù)發(fā)送 10 個(gè) Delay 2500 ms 的 Message
        for (i in 1..10) {
            sendDelayedMessage(mainHandler, i)
        }
        Log.d("MainActivity","testSendDelayedMessages() end ")
    }

第 1 個(gè) Message 執(zhí)行的時(shí)間差為 2505ms(39:56.841 - 39:54.336)昙啄,第 10 個(gè) Message 執(zhí)行的時(shí)間差已經(jīng)達(dá)到了 2508ms(39:56.844 - 39:54.336)。

09-22 22:39:54.116 25104 25104 D MainActivity: onCreate()
09-22 22:39:54.336 25104 25104 D MainActivity: testSendDelayedMessages() start
09-22 22:39:54.337 25104 25104 D MainActivity: testSendDelayedMessages() end
09-22 22:39:56.841 25104 25104 D MainActivity: Main thread message occurred & what:1
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:2
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:3
..
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:8
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:9
09-22 22:39:56.844 25104 25104 D MainActivity: Main thread message occurred & what:10

為什么塔沃?

延時(shí) Message 執(zhí)行的時(shí)刻 when 采用的是發(fā)送的時(shí)刻和 Delay 時(shí)長的累加,基于此排隊(duì)進(jìn) MessageQueue轻绞。

// Handler.java
    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {  
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        return enqueueMessage(queue, msg, uptimeMillis); 
    }

Delay Message 尚未抵達(dá)的時(shí)候,MessageQueue#next() 會(huì)將讀取隊(duì)列的時(shí)刻與 when 的差值佣耐,作為下一次通知 Native 休眠的時(shí)長政勃。進(jìn)行下一次循環(huán)前,next() 還存在其他邏輯兼砖,導(dǎo)致 wake up 的時(shí)刻存在滯后奸远。此外由于 wake up 后線程存在其他 Message 占用,導(dǎo)致執(zhí)行更加延后讽挟。

// MessageQueue.java
    Message next() {
        ...
        for (;;) {
            ...
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                ...
                
                if (msg != null) {
                    // 計(jì)算下一次循環(huán)應(yīng)當(dāng)休眠的時(shí)長
                    if (now < msg.when) {
                        nextPollTimeoutMillis
                            = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        ...
                    }
                } else {
                    ...
                }
                ...
            }
            ...
        }
    }

結(jié)論

由于喚醒時(shí)長的計(jì)算誤差和回調(diào)的任務(wù)可能占用線程懒叛,導(dǎo)致延時(shí)執(zhí)行 Message 不是時(shí)間到了就會(huì)執(zhí)行,其執(zhí)行的時(shí)刻必然晚于 Delay 的時(shí)刻耽梅。

插隊(duì)執(zhí)行 Message

Handler 還提供了 Message 插隊(duì)的 API:sendMessageAtFrontOfQueue()postAtFrontOfQueue()薛窥。

在上述的 send 和 post 之后同時(shí)調(diào)用 xxxFrontOfQueue 的方法,Message 的執(zhí)行結(jié)果會(huì)怎么樣眼姐?

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        testSendNoDelayedMessages()
        testFrontMessages() // 立馬調(diào)用 FrontOfQueue 的方法
    }

分別調(diào)用 sendMessageAtFrontOfQueue() 和 postAtFrontOfQueue() 的 API诅迷。

    private fun testFrontMessages() {
        Log.d("MainActivity","testFrontMessages() start")
        testSendFrontMessages()
        testPostFrontRunnable()
        Log.d("MainActivity","testFrontMessages() end ")
    }

    private fun testSendFrontMessages() {
        Log.d("MainActivity","testSendFrontMessages() start")
        for (i in 21..30) {
            sendMessageFront(mainHandler, i)
        }
        Log.d("MainActivity","testSendFrontMessages() end ")
    }

    private fun testPostFrontRunnable() {
        Log.d("MainActivity","testPostFrontRunnable() start")
        for (i in 31..40) {
            mainHandler.postAtFrontOfQueue() { Log.d("MainActivity", "testPostFrontRunnable() run & i:${i}") }
        }
        Log.d("MainActivity","testPostFrontRunnable() end ")
    }

當(dāng)主線程的打印日志按序輸出后,Message 開始逐個(gè)執(zhí)行众旗。按照預(yù)想的一樣罢杉,F(xiàn)rontOfQueue 的 Message 會(huì)先執(zhí)行,也就是最后一次調(diào)用這個(gè) API 的最早回調(diào)逝钥。

Front 的 Message 逆序執(zhí)行完畢之后屑那,普通的 Message 才按照請(qǐng)求的順序執(zhí)行。

 D MainActivity: testSendNoDelayedMessages() start
 D MainActivity: startSendMessage() start
 D MainActivity: startSendMessage() end
 D MainActivity: testPostRunnable() start
 D MainActivity: testPostRunnable() end
 D MainActivity: testSendNoDelayedMessages() end
 D MainActivity: testFrontMessages() start
 D MainActivity: testSendFrontMessages() start
 D MainActivity: testSendFrontMessages() end
 D MainActivity: testPostFrontRunnable() start
 D MainActivity: testPostFrontRunnable() end
 D MainActivity: testFrontMessages() end
 D MainActivity: testPostFrontRunnable() run & i:40
 ...
 D MainActivity: testPostFrontRunnable() run & i:31
 D MainActivity: Main thread message occurred & what:30
 ...
 D MainActivity: Main thread message occurred & what:21
 D MainActivity: Main thread message occurred & what:1
 ...
 D MainActivity: Main thread message occurred & what:10
 D MainActivity: testPostRunnable() run & i:11
 ...
 D MainActivity: testPostRunnable() run & i:20

怎么實(shí)現(xiàn)的艘款?

原理在于 sendMessageAtFrontOfQueue() 或 postAtFrontOfQueue() 發(fā)送的 Mesage 被記錄的 when 屬性被固定為 0持际。

// Handler.java
    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        return enqueueMessage(queue, msg, 0); // 發(fā)送的 when 等于 0
    }

    public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

從入隊(duì)函數(shù)可以看出,when 為 0 的 Message 會(huì)立即插入隊(duì)首哗咆,所以總會(huì)先得到執(zhí)行蜘欲。

// MessageQueue.java
    enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            // 如果 Message 需要插隊(duì)(sendMessageAtFrontOfQueue)
            // 則插入隊(duì)首
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
            } else {
                ...
            }
            ...
        }
        return true;
    }

結(jié)論

sendMessageAtFrontOfQueue() 和 postAtFrontOfQueue() 的 API 通過將 when 預(yù)設(shè)為 0 進(jìn)而插入 Message 至隊(duì)首,最終達(dá)到 Message 先得到執(zhí)行的目的晌柬。

但需要注意的是姥份,這將造成本來先該執(zhí)行的 Message 被延后調(diào)度,對(duì)于存在先后關(guān)系的業(yè)務(wù)邏輯來說將可能造成順序問題年碘,謹(jǐn)慎使用澈歉!

異步執(zhí)行 Message

Handler 發(fā)送的 Message 都是同步的,意味著大家都按照 when 的先后進(jìn)行排序屿衅,誰先到誰執(zhí)行埃难。

如果遇到優(yōu)先級(jí)高的 Message 可以通過 FrontQueue 發(fā)送插隊(duì) Message即可。但如果是希望同步的隊(duì)列停滯只執(zhí)行指定 Message 的話,即 Message 異步執(zhí)行涡尘,現(xiàn)有的 API 是不夠的忍弛。

事實(shí)上 Android 提供了同步屏障的機(jī)制來實(shí)現(xiàn)這一需求,不過主要面向的是系統(tǒng) App 或 系統(tǒng)考抄,App 可以通過反射來使用细疚。

通過異步 Handler 實(shí)現(xiàn)

除了一般使用的 Handler 構(gòu)造函數(shù)以外,Handler 還提供了創(chuàng)建發(fā)送異步 Message 的專用構(gòu)造函數(shù)川梅。通過該 Handler 發(fā)送的 Message 或 Runnable 都是異步的疯兼。我們將其稱為異步 Handler

// Handler.java
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    @NonNull
    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        if (callback == null) throw new NullPointerException("callback must not be null");
        return new Handler(looper, callback, true);
    }

我們啟動(dòng)一個(gè) HandlerThread 來測試一下同步屏障的使用:分別構(gòu)建一個(gè)普通 Handler異步 Handler挑势。

    private fun startBarrierThread() {
        val handlerThread = HandlerThread("Test barrier thread")
        handlerThread.start()

        normalHandler = Handler(handlerThread.looper) { msg ->
            Log.d(...)
            true
        }

        barrierHandler = Handler.createAsync(handlerThread.looper) { msg ->
            Log.d(...)
            true
        }
    }

啟動(dòng) HandlerThread 并向兩個(gè) Handler 各發(fā)送一個(gè) Message镇防。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        startBarrierThread()
        testNormalMessage()
        testSyncBarrierByHandler()
    }
    
    private fun testNormalMessage() {
        sendMessageRightNow(normalHandler, 1)
    }

    private fun testSyncBarrierByHandler() {
        sendMessageRightNow(barrierHandler, 2)
    }

是異步 Handler 的 Message 先執(zhí)行嗎?非也潮饱,因?yàn)槲覀冞€沒有通知 MessageQueue 建立同步屏障!

09-24 23:02:19.032 28113 28113 D MainActivity: onCreate()
09-24 23:02:19.150 28113 28141 D MainActivity: Normal handler message occurred & what:1
09-24 23:02:19.150 28113 28141 D MainActivity: Barrier handler message occurred & what:2

除了發(fā)送異步Handler 發(fā)送異步 Message 以外诫给,需要通過反射事先建立起同步屏障香拉。

注意:建立同步屏障必須早于需要屏蔽的同步 Message,否則無效中狂,后面的原理會(huì)提及凫碌。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        startBarrierThread()
        // 建立一個(gè)同步屏障
        postSyncBarrier(barrierHandler.looper)
        testNormalMessage()
        testSyncBarrierByHandler()
    }

    private fun postSyncBarrier(looper: Looper) {
        Log.d(...)
        val method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier")
        barrierToken = method.invoke(looper.queue) as Int
    }

這樣子便可以看到,異步 Message 執(zhí)行了胃榕,而且同步 Message 永遠(yuǎn)得不到執(zhí)行盛险。

09-24 23:11:36.176 28600 28600 D MainActivity: onCreate()
09-24 23:11:36.296 28600 28600 D MainActivity: Add sync barrier
09-24 23:11:36.300 28600 28629 D MainActivity: Barrier handler message occurred & what:2

原因在于建立的同步屏障尚未移除,永遠(yuǎn)只處理隊(duì)列里的異步 Message勋又。想要讓同步 Message 恢復(fù)執(zhí)行的話 remove 同步屏障即可苦掘,同樣也需要反射!

我們?cè)诋惒?Handler 執(zhí)行結(jié)束后移除同步屏障楔壤。

    private fun startBarrierThread() {
        ...
        barrierHandler = Handler.createAsync(handlerThread.looper) { msg ->
            Log.d(...)
            // 移除同步屏障
            removeSyncBarrier(barrierHandler.looper)
            true
        }
    }

    fun removeSyncBarrier(looper: Looper) {
        Log.d(...)
        val method = MessageQueue::class.java
            .getDeclaredMethod("removeSyncBarrier", Int::class.javaPrimitiveType)
        method.invoke(looper.queue, barrierToken)
    }

可以看到同步 Message 恢復(fù)了鹤啡。

09-24 23:10:31.533 28539 28539 D MainActivity: onCreate()
09-24 23:10:31.652 28539 28568 D MainActivity: Barrier handler message occurred & what:2
09-24 23:10:31.652 28539 28568 D MainActivity: Remove sync barrier
09-24 23:10:31.653 28539 28568 D MainActivity: Normal handler message occurred & what:1

通過異步 Message 實(shí)現(xiàn)

沒有專用的異步 Handler 的時(shí)候,可以向普通 Handler 發(fā)送一個(gè) isAsync 屬性為 true 的 message蹲嚣,效果和異步 Handler 是一樣的递瑰。當(dāng)然這種方式仍舊需要建立同步屏障。

在原有的發(fā)送 Message 的函數(shù)里加入 isAsync 的重載參數(shù)隙畜。

    private fun sendMessageRightNow(handler: Handler, what: Int, isAsync: Boolean = false) {
        Message.obtain().let {
            it.what = what
            it.isAsynchronous = isAsync
            handler.sendMessage(it)
        }
    }

向普通 Handler 發(fā)送異步 Message抖部。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        testNormalMessage()
        // 改用 Message 方式發(fā)送異步 Message
        testSyncBarrierByMessage()
    }

    private fun testSyncBarrierByMessage() {
        sendMessageRightNow(normalHandler, 2, true)
    }

同樣記得在異步 Message 收到后移除同步屏障。

    private fun startBarrierThread() {
        ...
        normalHandler = Handler(handlerThread.looper) { msg ->
            Log.d(...)
            if (2 == msg.what) removeSyncBarrier(barrierHandler.looper)
            true
        }
    }

結(jié)果和異步 Handler 的方式一致议惰。

09-24 23:58:05.801 29040 29040 D MainActivity: onCreate()
09-24 23:58:05.923 29040 29040 D MainActivity: Add sync barrier
09-24 23:58:05.923 29040 29070 D MainActivity: Normal handler message occurred & what:2
09-24 23:58:05.924 29040 29070 D MainActivity: Remove sync barrier
09-24 23:58:05.924 29040 29070 D MainActivity: Normal handler message occurred & what:1

原理

先來看一下同步屏障是怎么建立的慎颗。

// MessageQueue.java
    // 默認(rèn)是調(diào)用的時(shí)刻開始建立屏障
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    // 同步屏障支持指定開始的時(shí)刻
    // 默認(rèn)是調(diào)用的時(shí)刻,而 0 表示?
    private int postSyncBarrier(long when) {
        synchronized (this) {
            // 同步屏障可以建立多個(gè)哗总,用計(jì)數(shù)的 Token 變量識(shí)別
            final int token = mNextBarrierToken++;

            // 獲取一個(gè)屏障 Message
            // 其 target 屬性為空
            // 指定 when 屬性為屏障的開始時(shí)刻
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            // 將 Token 存入屏障 Message
            // 用以識(shí)別對(duì)應(yīng)的同步屏障
            msg.arg1 = token;

            // 按照 when 的先后
            // 找到屏障 Message 插入隊(duì)列的適當(dāng)位置
            // 所以几颜,如果同步屏障的建立調(diào)用得晚
            // 那么在它之前的 Message 無法阻攔
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }

            // 將屏障 Message 插入
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                // 如果隊(duì)列尚無 Message
                // 或隊(duì)首的 Message 時(shí)刻
                // 都比屏障 Message 要晚的話
                // 將屏障 Message 插入隊(duì)首
                msg.next = p;
                mMessages = msg;
            }
            
            // 返回上面的 Token 給調(diào)用端
            // 主要用于移除對(duì)應(yīng)的屏障
            return token;
        }
    }

再來看下異步 Message 如何執(zhí)行。

// MessageQueue.java
    Message next() {
        ...
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 隊(duì)首是屏障 Message 的話
                // 遍歷找到下一個(gè)異步 Message
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                
                // 沒有建立同步屏障且隊(duì)里有 Message
                // 或者
                // 建立了同步屏障下且找到了異步 Message
                if (msg != null) {
                    // 如果當(dāng)前時(shí)間尚早于目標(biāo)執(zhí)行時(shí)刻
                    if (now < msg.when) {
                        // 更新下次循環(huán)應(yīng)當(dāng)休眠的超時(shí)時(shí)間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        mBlocked = false;
                        
                        // Message 找到了并出隊(duì)
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }

                        // Message 返回
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 隊(duì)里尚無 Message
                    // 或建立了同步屏障讯屈,但尚無異步 Message
                    // 無限休眠
                    nextPollTimeoutMillis = -1;
                }
                ...
            }
            ...
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

最后看一下同步屏障如何移除蛋哭。

// MessageQueue.java
    // 需要傳入 add 時(shí)返回的 Token
    public void removeSyncBarrier(int token) {
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            // 遍歷隊(duì)列直到找到 token 吻合的屏障 Message
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            
            // 如果沒找到會(huì)拋出異常
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            
            // 將屏障 Message 移除
            
            // 如果屏障 Message 不在隊(duì)首的話
            // 無需喚醒
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                // 屏障 Message 在隊(duì)首
                // 且新的隊(duì)首存在且不是另一個(gè)屏障的話
                // 需要立即喚醒
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // 喚醒以立即處理后面的 Message
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

對(duì)原理進(jìn)行簡單的總結(jié):

  1. 同步屏障的建立:按照調(diào)用的時(shí)刻 when 在合適的位置放入一個(gè)屏障 Message(target 屬性為 null)來實(shí)現(xiàn),同時(shí)得到標(biāo)識(shí)屏障的計(jì)數(shù) token 存入屏障 Message
  2. 讀取隊(duì)列的時(shí)候發(fā)現(xiàn)存在屏障 Message 的話涮母,會(huì)遍歷隊(duì)列并返回最早執(zhí)行的異步 Message
  3. 同步屏障的移除:按照 token 去隊(duì)列里找到匹配的屏障 Message 進(jìn)行出隊(duì)操作谆趾,如果出隊(duì)后隊(duì)首存在 Message 且非另一個(gè)同步屏障的話,立即喚醒 looper 線程

結(jié)論和應(yīng)用

結(jié)論:

  • 可以通過異步 Handler叛本,也可以通過異步 Message 兩種方式向 MessageQueue 添加異步 Message
  • 但都需要事先建立同步屏障沪蓬,屏障的建立時(shí)間必須在阻攔的 Message 發(fā)出之前
  • 可以建立多個(gè)同步屏障,將按照指定的時(shí)刻排隊(duì)来候,通過計(jì)數(shù) Token 進(jìn)行識(shí)別
  • 同步屏障使用完之后記得移除跷叉,否則后續(xù)的 Message 永遠(yuǎn)阻塞

和插隊(duì)執(zhí)行 Message 的區(qū)別:

  • 插隊(duì) Message 只能確保先執(zhí)行,完了后續(xù)的 Message 還得執(zhí)行
  • 異步 Message 則不同营搅,同步屏障一旦建立將保持休眠云挟,直到異步 Message 抵達(dá)。只有同步屏障被撤銷转质,后續(xù) Message 才可恢復(fù)執(zhí)行

應(yīng)用:

AOSP 系統(tǒng)中使用異步 Message 最典型的地方要屬屏幕刷新园欣,刷新任務(wù)的 Message 不希望被主線程的 Message 隊(duì)列阻塞,所以在發(fā)送刷新 Message 之前都會(huì)建立一個(gè)同步屏障休蟹,確保刷新任務(wù)優(yōu)先執(zhí)行沸枯。

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

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

屏障建立之后發(fā)送異步 Message。

// Choreographer.java
    private void postCallbackDelayedInternal(...) {
        synchronized (mLock) {
            ...

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

空閑執(zhí)行 “Message”

MessageQueue 提供的 IdleHandler 可以讓隊(duì)列在空閑的時(shí)候回調(diào)(queueIdle())指定的邏輯赂弓,它本質(zhì)上不是 Message 類型绑榴,但它在 MessageQueue 里調(diào)度的時(shí)候類似于 Message 的邏輯,姑且將它也理解成一種特殊的 ”Message“拣展。

使用上很簡單彭沼,調(diào)用 MessageQueue 的 addIdleHandler() 添加實(shí)現(xiàn)即可,執(zhí)行完之后無需再度執(zhí)行的話需要調(diào)用 removeIdleHandler() 移除备埃,或在回調(diào)里返回 false姓惑。

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        testIdleHandler()
    }

    private fun testIdleHandler() {
        Log.d("MainActivity","testIdleHandler() start")
        mainHandler.looper.queue.addIdleHandler {
            Log.d("MainActivity", "testIdleHandler() queueIdle callback")
            false
        }
        Log.d("MainActivity","testIdleHandler() end ")
    }

可以看到 addIdleHandler 調(diào)用之后并沒有立即執(zhí)行,而是過了幾百 ms按脚,queueIdle() 才得到了執(zhí)行于毙。

09-23 22:56:46.130  7732  7732 D MainActivity: onCreate()
09-23 22:56:46.281  7732  7732 D MainActivity: testIdleHandler() start
09-23 22:56:46.281  7732  7732 D MainActivity: testIdleHandler() end
09-23 22:56:46.598  7732  7732 D MainActivity: testIdleHandler() queueIdle callback

如果在 addIdleHandler 調(diào)用之后接著發(fā)送一串非延時(shí) Message,queueIdle() 是先執(zhí)行還是后執(zhí)行呢辅搬?

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        testIdleHandler()
        testSendMessages()
    }

結(jié)果顯示一堆 Message 執(zhí)行完了之后唯沮,仍舊過了幾百 ms脖旱,queueIdle() 才得到了執(zhí)行。

09-23 23:07:50.639  7926  7926 D MainActivity: onCreate()
09-23 23:07:50.856  7926  7926 D MainActivity: testIdleHandler() start
09-23 23:07:50.856  7926  7926 D MainActivity: testIdleHandler() end
09-23 23:07:50.856  7926  7926 D MainActivity: startSendMessage() start
09-23 23:07:50.857  7926  7926 D MainActivity: startSendMessage() end
09-23 23:07:50.914  7926  7926 D MainActivity: Main thread message occurred & what:1
...
09-23 23:07:50.916  7926  7926 D MainActivity: Main thread message occurred & what:10
09-23 23:07:51.132  7926  7926 D MainActivity: testIdleHandler() queueIdle callback

上述結(jié)果也可以理解介蛉,MessageQueue 里仍有一堆 Message 等待處理萌庆,并非空閑狀態(tài)。所以需要執(zhí)行完之后才有機(jī)會(huì)回調(diào) queueIdle() 币旧。

那如果發(fā)送的是延時(shí) Message 呢践险?

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    testIdleHandler()
    testSendDelayedMessages()
}

因?yàn)榘l(fā)送的是延時(shí) Message,MessageQueue 暫時(shí)是空閑的吹菱,會(huì)先將 IdleHandler 取出來處理巍虫。

09-23 23:21:36.135  8161  8161 D MainActivity: onCreate()
09-23 23:21:36.339  8161  8161 D MainActivity: testIdleHandler() start
09-23 23:21:36.340  8161  8161 D MainActivity: testIdleHandler() end
09-23 23:21:36.340  8161  8161 D MainActivity: testSendDelayedMessages() start
09-23 23:21:36.340  8161  8161 D MainActivity: testSendDelayedMessages() end
09-23 23:21:36.729  8161  8161 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:21:38.844  8161  8161 D MainActivity: Main thread message occurred & what:1
...
09-23 23:21:38.845  8161  8161 D MainActivity: Main thread message occurred & what:10

上面的 queueIdle() 返回了 false 確保處理后 Handler 得到了移除。

但如果返回 true 且沒有調(diào)用 removeIdleHandler() 的話鳍刷,后續(xù)空閑的時(shí)候 Handler 還會(huì)被執(zhí)行占遥,這點(diǎn)需要留意!

    private fun testIdleHandler() {
        mainHandler.looper.queue.addIdleHandler {
            ...
            true // false
        }
    }

queueIdle() 因?yàn)闆]被移除的緣故被回調(diào)了多次输瓜,源自于 Looper 沒執(zhí)行完一次 Message 后發(fā)現(xiàn)尚無 Message 的時(shí)候都會(huì)回調(diào)一遍 IdleHandler瓦胎,直到隊(duì)列一直沒有 Message 到來。

09-23 23:24:04.765  8226  8226 D MainActivity: onCreate()
09-23 23:24:05.010  8226  8226 D MainActivity: testIdleHandler() start
09-23 23:24:05.011  8226  8226 D MainActivity: testIdleHandler() end
09-23 23:24:05.368  8226  8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.370  8226  8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.378  8226  8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.381  8226  8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.459  8226  8226 D MainActivity: testIdleHandler() queueIdle callback

那如果 add 完不移除的 IdleHandler 之后尤揣,發(fā)送一個(gè)延時(shí) Message凛捏,那便會(huì)導(dǎo)致空閑消息多執(zhí)行一遍。

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    testIdleHandler()
    sendDelayedMessage(mainHandler, 1)
}

09-23 23:31:53.928  8620  8620 D MainActivity: onCreate()
09-23 23:31:54.042  8620  8620 D MainActivity: testIdleHandler() start
09-23 23:31:54.042  8620  8620 D MainActivity: testIdleHandler() end
09-23 23:31:54.272  8620  8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.273  8620  8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.278  8620  8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.307  8620  8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.733  8620  8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:56.546  8620  8620 D MainActivity: Main thread message occurred & what:1
09-23 23:31:56.546  8620  8620 D MainActivity: testIdleHandler() queueIdle callback

為什么芹缔?

queueIdle() 的回調(diào)由 MessageQueue#next() 回調(diào)。

// MessageQueue.java
    Message next() {
        ...
        // 循環(huán)的初次將待處理 IdleHandler 計(jì)數(shù)置為 -1
        // 保證第一次可以檢查 Idle Handler 的存在和調(diào)用
        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 隊(duì)首的 Message 且建立了同步屏障的話瓶盛,尋找下一個(gè)異步 Message
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                
                // 找到了合適的 Message
                if (msg != null) {
                    // 如果當(dāng)前時(shí)間尚早于目標(biāo)執(zhí)行時(shí)刻
                    // 設(shè)置休眠的超時(shí)時(shí)間最欠,即當(dāng)前時(shí)間與目標(biāo)時(shí)刻的差值
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        mBlocked = false;
                        
                        // 時(shí)間條件滿足 Message 出隊(duì)
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }

                        // 并返回 Message
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 隊(duì)里尚無合適的 Message
                    // 進(jìn)入無限休眠
                    nextPollTimeoutMillis = -1;
                }

                // 如果正在退出 Looper,結(jié)束循環(huán)并返回 null
                // 將促使 loop() 退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 如果沒有合適的 Message 且 Looper 沒有退出
                // 檢查是否有 Idle Handler 需要處理

                // 讀取 Idle Handler 列表
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                
                // 如果暫時(shí)沒有 Idle Handler 需要處理惩猫,則進(jìn)入下一次循環(huán)
                // 為使下次循環(huán)如果出現(xiàn)新的 Idle Handler 能有機(jī)會(huì)執(zhí)行
                // 不重置計(jì)數(shù)器芝硬,仍為初始值 -1
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

                // 如果 IdleHandler 存在則拷貝到待處理列表
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 遍歷待處理 Idle Handlers
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null;

                boolean keep = false;
                try {
                    // 逐個(gè)回調(diào) queueIdle()
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                // 回調(diào)返回 false,則將其移除出 Idle 列表
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 處理完之后重置 IdleHandler 的計(jì)數(shù)
            // 保證下次循環(huán)不會(huì)重復(fù)處理 IdleHandler
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

有幾點(diǎn)細(xì)節(jié)需要留意:

  1. next() 循環(huán)的第一次將 count 置為 -1轧房,確保隊(duì)列空閑的時(shí)候必然有機(jī)會(huì)處理 IdleHandler
  2. 如果暫無 IdleHandler 可以處理直接進(jìn)入下一次循環(huán)拌阴,并且保留 count 的處置,確保下次循環(huán)可以檢查是否有新的 IdleHandler 加入進(jìn)來
  3. IdleHandler 正常處理結(jié)束之后奶镶,避免下次循環(huán)重復(fù)處理迟赃,會(huì)將 count 置為 0,保證下次不再檢查厂镇。注意:是下次循環(huán)纤壁,不是永久不檢查

結(jié)論和應(yīng)用

結(jié)論: IdleHandler 可以實(shí)現(xiàn) MessageQueue 空閑狀態(tài)下的任務(wù)執(zhí)行,比如做一些啟動(dòng)時(shí)的輕量級(jí)初始化任務(wù)捺信。但由于其執(zhí)行的時(shí)機(jī)依賴于隊(duì)列的 Message 狀態(tài)酌媒,不太可控,謹(jǐn)慎使用!

應(yīng)用:AOSP 源碼里有不少地方使用了 IdleHandler 機(jī)制秒咨,比如 ActivityThread 使用它在空閑的狀態(tài)下進(jìn)行 GC 回收處理喇辽。

// ActivityThread.java
    final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            purgePendingResources();
            return false;
        }
    }
    
    void scheduleGcIdler() {
        if (!mGcIdlerScheduled) {
            mGcIdlerScheduled = true;
            Looper.myQueue().addIdleHandler(mGcIdler);
        }
        mH.removeMessages(H.GC_WHEN_IDLE);
    }

    void unscheduleGcIdler() {
        if (mGcIdlerScheduled) {
            mGcIdlerScheduled = false;
            Looper.myQueue().removeIdleHandler(mGcIdler);
        }
        mH.removeMessages(H.GC_WHEN_IDLE);
    }

名詞回顧

非延時(shí)、延時(shí)以及插隊(duì)執(zhí)行這幾種 Message 大家使用較多雨席,無需贅述菩咨。但其他幾個(gè)冷僻的 Message 術(shù)語需要總結(jié)一下,供大家快速對(duì)比和加深理解舅世。

Message 術(shù)語 介紹
異步 Message isAsync 屬性為 true 需要異步執(zhí)行的 Message旦委,需要配合同步屏障使用
異步 Handler 專門發(fā)送異步 Message 的 Handler
屏障 Message target 為空并持有 token 信息的 Message 實(shí)例放入隊(duì)列,作為同步屏障的起點(diǎn)
同步屏障 在 MessageQueue 指定時(shí)刻插入屏障 Message 確保只有異步 Message 執(zhí)行的機(jī)制
空閑 IdleHandler 用于在 MessageQueue 空閑的時(shí)候回調(diào)的處理接口雏亚,若不移除每次隊(duì)列空閑了均會(huì)執(zhí)行

結(jié)語

上述對(duì)于各種 Message 和 IdleHandler 做了演示和原理闡述缨硝,相信對(duì)于它的細(xì)節(jié)有了更深的了解。

下面來進(jìn)行一個(gè)簡單的總結(jié):

  • 非延時(shí)執(zhí)行 Message:并非立即執(zhí)行罢低,而是按照請(qǐng)求的時(shí)刻進(jìn)行排隊(duì)和調(diào)度查辩,最終取決于隊(duì)列的順序和主線程是否空閑
  • 延時(shí)執(zhí)行 Message:也并非在 Delay 的時(shí)刻立即執(zhí)行,執(zhí)行時(shí)刻受喚醒誤差和線程任務(wù)阻塞的影響必然晚于 Delay 時(shí)刻
  • 插隊(duì)執(zhí)行 Mesage:同樣并非立即執(zhí)行网持,而是每次都將任務(wù)放在了隊(duì)首宜岛,達(dá)到先執(zhí)行的目的,但打亂了執(zhí)行順序存在邏輯隱患
  • 異步 Message:系統(tǒng)使用居多功舀,App 則需反射萍倡,通過這種機(jī)制可插隊(duì)執(zhí)行同時(shí)確保其他 Message 阻塞,學(xué)習(xí)一下
  • IdleHandler “Message”:系統(tǒng)多有使用辟汰,實(shí)現(xiàn) MessageQueue 空閑狀態(tài)下的任務(wù)執(zhí)行列敲,但執(zhí)行時(shí)機(jī)不可控,最好執(zhí)行完之后便移除帖汞,謹(jǐn)慎使用
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戴而,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子翩蘸,更是在濱河造成了極大的恐慌所意,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件催首,死亡現(xiàn)場離奇詭異扶踊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翅帜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門姻檀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涝滴,你說我怎么就攤上這事绣版〗禾ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵杂抽,是天一觀的道長诈唬。 經(jīng)常有香客問我,道長缩麸,這世上最難降的妖魔是什么铸磅? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮杭朱,結(jié)果婚禮上阅仔,老公的妹妹穿的比我還像新娘。我一直安慰自己弧械,他們只是感情好八酒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刃唐,像睡著了一般羞迷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上画饥,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天衔瓮,我揣著相機(jī)與錄音,去河邊找鬼抖甘。 笑死热鞍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衔彻。 我是一名探鬼主播碍现,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼米奸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起爽篷,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤悴晰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逐工,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铡溪,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年泪喊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棕硫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袒啼,死狀恐怖哈扮,靈堂內(nèi)的尸體忽然破棺而出纬纪,到底是詐尸還是另有隱情,我是刑警寧澤滑肉,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布包各,位于F島的核電站,受9級(jí)特大地震影響靶庙,放射性物質(zhì)發(fā)生泄漏问畅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一六荒、第九天 我趴在偏房一處隱蔽的房頂上張望护姆。 院中可真熱鬧,春花似錦掏击、人聲如沸卵皂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渐裂。三九已至,卻和暖如春钠惩,著一層夾襖步出監(jiān)牢的瞬間柒凉,已是汗流浹背著隆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工隐砸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人序芦。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓愧沟,卻偏偏與公主長得像蔬咬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沐寺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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