Android訊息機制完整的執行流程,瞭解一下

語言: CN / TW / HK

highlight: vs theme: devui-blue


持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第9天,點選檢視活動詳情

經過前面幾篇文章的鋪墊,介紹了HanlderMessage等類相關使用,分析了其與LooperMessageQueue的部分原始碼,本篇文章主要是集中梳理Android整個訊息機制執行的完整流程。

Handler.post()說起

Handler.post()是用來發送訊息的,我們看下Handler原始碼的處理:

java public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }

首先會呼叫到getPostMessage()方法將Runnable封裝成一條Message,然後緊接著呼叫sendMessageDelayed()方法:

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

這裡我們介紹下sendMessageDelayed()的第二個引數delayMillis,這個表示訊息延時執行的時間,而post()方法本身代表著非延遲執行,所以這裡delayMillis的值為0.

而如果是我們另一個常用的函式postDelay(),這裡的delayMillis的值就是傳入的延遲執行的時間

繼續往下走,會呼叫到Handler.sendMessageAtTime()方法:

java public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; //... return enqueueMessage(queue, msg, uptimeMillis); }

獲取到Looper對應的訊息佇列MessageQueue,繼續往下走,作為引數傳給enqueueMessage()方法,這個方法主要是對上面封裝的Message進行填充:

```java private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
    msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);

} ```

比如將Message被負責分發的target賦值成當前Handler物件,然後根據是否為非同步Handler來決定是否給Message新增非同步標識。

MessageQueue.enqueueMessage()新增訊息至佇列中

java boolean enqueueMessage(Message msg, long when) { //... synchronized (this) { //... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; //1. if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //2. for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } //3. if (needWake) { nativeWake(mPtr); } } return true; }

這個方法的使用很明確,就是將Message新增到訊息佇列中,下來我們主要講解這個方法的三個核心點,對應上面的註釋標識:

  1. 如果當前訊息佇列本來為null、訊息執行的時間戳為0、訊息執行的時間小於訊息佇列隊頭訊息的執行時間,只要滿足上面三個條件之一,直接將該條Message新增到訊息佇列隊頭;

這裡說下訊息執行的時間戳什麼時候會為0,就是呼叫Handler.sendMessageAtFrontOfQueue()這個方法,就會觸發將當前傳送的Message新增到訊息佇列隊頭。

  1. 如果上面的三個條件都不滿足,就遍歷訊息佇列,比較將要傳送的訊息和訊息佇列的訊息執行時間戳when,選擇適當的位置插入;

  2. 判斷是否需要喚醒當前主執行緒,開始從訊息佇列獲取訊息進行執行;

Looper.loop()分發訊息

這個方法會開啟一個for(;;)迴圈,不斷的從訊息佇列中獲取訊息分發執行,沒有訊息時會阻塞主執行緒進行休眠,讓出CPU執行權。

for(;;)迴圈會不斷的呼叫Looper.loopOnce(),開始真正的訊息獲取和分發執行:

java private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block if (msg == null) { return false; } try { msg.target.dispatchMessage(msg); } msg.recycleUnchecked(); return true; }

上面是經過簡化的程式碼,首先呼叫MessageQueue.next()從訊息佇列中獲取訊息,然後呼叫關鍵方法msg.target.dispatchMessage(msg)開始訊息的分發執行,這個方法之前的文章有進行介紹,這裡就不再過多介紹了。

接下來我們看下MessageQueue.next()如何獲取訊息的。

MessageQueue.next()獲取訊息

```java Message next() { //... for (;;) { //1.休眠主執行緒 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //2.獲取非同步訊息 if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } //3.獲取普通訊息 if (msg != null) { if (now < msg.when) { nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } else { nextPollTimeoutMillis = -1; }

        //...
        if (mPendingIdleHandlers == null) {
            mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
        }
        mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    }
    //4.執行Idle訊息
    for (int i = 0; i < pendingIdleHandlerCount; i++) {
        final IdleHandler idler = mPendingIdleHandlers[i];
        mPendingIdleHandlers[i] = null;

        boolean keep = idler.queueIdle();
        if (!keep) {
            synchronized (this) {
                mIdleHandlers.remove(idler);
            }
        }
    }
    //...
}

} ```

  1. 如果當前訊息佇列中沒有訊息或者還沒到下一條訊息的執行時間,就呼叫nativePollOnce()方法休眠主執行緒,讓出CPU執行權;

  2. 如果Message的target為null,就代表是一個訊息屏障訊息,之後就只能從訊息佇列獲取非同步訊息了,如果不存在,就嘗試執行Idle訊息;

  3. 如果不存在訊息屏障,則就從訊息佇列中正常嘗試獲取Message,如果不存在,就嘗試執行Idle訊息;

  4. 執行Idle訊息,只有在主執行緒空閒(當前訊息佇列中沒有訊息或者還沒到下一條訊息的執行時間)的情況下才會去嘗試執行Idle訊息,這種型別的訊息非常有用,具體的可以參考我之前寫的文章:IdleHandler基本使用及應用案例分析

總結

本篇文章主要是詳細分析了Android訊息機制的整個執行流程(不包括native層),最核心的就是HandlerLooperMessageQueueMessage四個類及構成的關聯,希望能給你帶來幫助。