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四個類及構成的關聯,希望能給你帶來幫助。