日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

詳解Android消息機制完整的執(zhí)行流程_Android

作者:長安皈故里 ? 更新時間: 2022-11-23 編程語言

從Handler.post()說起

Handler.post()是用來發(fā)送消息的,我們看下Handler源碼的處理:

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

首先會調(diào)用到getPostMessage()方法將Runnable封裝成一條Message,然后緊接著調(diào)用sendMessageDelayed()方法:

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

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

而如果是我們另一個常用的函數(shù)postDelay(),這里的delayMillis的值就是傳入的延遲執(zhí)行的時間

繼續(xù)往下走,會調(diào)用到Handler.sendMessageAtTime()方法:

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

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

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被負責分發(fā)的target賦值成當前Handler對象,然后根據(jù)是否為異步Handler來決定是否給Message添加異步標識。

MessageQueue.enqueueMessage()添加消息至隊列中

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、消息執(zhí)行的時間戳為0、消息執(zhí)行的時間小于消息隊列隊頭消息的執(zhí)行時間,只要滿足上面三個條件之一,直接將該條Message添加到消息隊列隊頭;

這里說下消息執(zhí)行的時間戳什么時候會為0,就是調(diào)用Handler.sendMessageAtFrontOfQueue()這個方法,就會觸發(fā)將當前發(fā)送的Message添加到消息隊列隊頭。

2.如果上面的三個條件都不滿足,就遍歷消息隊列,比較將要發(fā)送的消息和消息隊列的消息執(zhí)行時間戳when,選擇適當?shù)奈恢貌迦耄?/p>

3.判斷是否需要喚醒當前主線程,開始從消息隊列獲取消息進行執(zhí)行;

Looper.loop()分發(fā)消息

這個方法會開啟一個for(;;)循環(huán),不斷的從消息隊列中獲取消息分發(fā)執(zhí)行,沒有消息時會阻塞主線程進行休眠,讓出CPU執(zhí)行權。

for(;;)循環(huán)會不斷的調(diào)用Looper.loopOnce(),開始真正的消息獲取和分發(fā)執(zhí)行:

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

上面是經(jīng)過簡化的代碼,首先調(diào)用MessageQueue.next()從消息隊列中獲取消息,然后調(diào)用關鍵方法msg.target.dispatchMessage(msg)開始消息的分發(fā)執(zhí)行,這個方法之前的文章有進行介紹,這里就不再過多介紹了。

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

MessageQueue.next()獲取消息

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.執(zhí)行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);
                }
            }
        }
        //...
    }
}
  • 如果當前消息隊列中沒有消息或者還沒到下一條消息的執(zhí)行時間,就調(diào)用nativePollOnce()方法休眠主線程,讓出CPU執(zhí)行權;
  • 如果Message的target為null,就代表是一個消息屏障消息,之后就只能從消息隊列獲取異步消息了,如果不存在,就嘗試執(zhí)行Idle消息;
  • 如果不存在消息屏障,則就從消息隊列中正常嘗試獲取Message,如果不存在,就嘗試執(zhí)行Idle消息;
  • 執(zhí)行Idle消息,只有在主線程空閑(當前消息隊列中沒有消息或者還沒到下一條消息的執(zhí)行時間)的情況下才會去嘗試執(zhí)行Idle消息,這種類型的消息非常有用,具體的可以參考我之前寫的文章:IdleHandler基本使用及應用案例分析

總結

本篇文章主要是詳細分析了Android消息機制的整個執(zhí)行流程(不包括native層),最核心的就是HandlerLooperMessageQueueMessage四個類及構成的關聯(lián)。

原文鏈接:https://juejin.cn/post/7152667953161322510

欄目分類
最近更新