詳解Android消息機(jī)制完整的執(zhí)行流程
從Handler.post()說(shuō)起
Handler.post()
是用來(lái)發(fā)送消息的,我們看下Handler
源碼的處理:
public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
首先會(huì)調(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()
的第二個(gè)參數(shù)delayMillis
,這個(gè)表示消息延時(shí)執(zhí)行的時(shí)間,而post()
方法本身代表著非延遲執(zhí)行,所以這里delayMillis
的值為0.
而如果是我們另一個(gè)常用的函數(shù)postDelay()
,這里的delayMillis
的值就是傳入的延遲執(zhí)行的時(shí)間
。
繼續(xù)往下走,會(huì)調(diào)用到Handler.sendMessageAtTime()
方法:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; //... return enqueueMessage(queue, msg, uptimeMillis); }
獲取到Looper
對(duì)應(yīng)的消息隊(duì)列MessageQueue
,繼續(xù)往下走,作為參數(shù)傳給enqueueMessage()
方法,這個(gè)方法主要是對(duì)上面封裝的Message
進(jìn)行填充:
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ù)責(zé)分發(fā)的target
賦值成當(dāng)前Handler
對(duì)象,然后根據(jù)是否為異步Handler
來(lái)決定是否給Message
添加異步標(biāo)識(shí)。
MessageQueue.enqueueMessage()添加消息至隊(duì)列中
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; }
這個(gè)方法的使用很明確,就是將Message
添加到消息隊(duì)列中,下來(lái)我們主要講解這個(gè)方法的三個(gè)核心點(diǎn),對(duì)應(yīng)上面的注釋標(biāo)識(shí):
1.如果當(dāng)前消息隊(duì)列本來(lái)為null、消息執(zhí)行的時(shí)間戳為0、消息執(zhí)行的時(shí)間小于消息隊(duì)列隊(duì)頭消息的執(zhí)行時(shí)間,只要滿足上面三個(gè)條件之一,直接將該條Message
添加到消息隊(duì)列隊(duì)頭;
這里說(shuō)下消息執(zhí)行的時(shí)間戳什么時(shí)候會(huì)為0,就是調(diào)用Handler.sendMessageAtFrontOfQueue()
這個(gè)方法,就會(huì)觸發(fā)將當(dāng)前發(fā)送的Message
添加到消息隊(duì)列隊(duì)頭。
2.如果上面的三個(gè)條件都不滿足,就遍歷消息隊(duì)列,比較將要發(fā)送的消息和消息隊(duì)列的消息執(zhí)行時(shí)間戳when
,選擇適當(dāng)?shù)奈恢貌迦耄?/p>
3.判斷是否需要喚醒當(dāng)前主線程,開(kāi)始從消息隊(duì)列獲取消息進(jìn)行執(zhí)行;
Looper.loop()分發(fā)消息
這個(gè)方法會(huì)開(kāi)啟一個(gè)for(;;)循環(huán)
,不斷的從消息隊(duì)列中獲取消息分發(fā)執(zhí)行,沒(méi)有消息時(shí)會(huì)阻塞主線程進(jìn)行休眠,讓出CPU執(zhí)行權(quán)。
for(;;)循環(huán)
會(huì)不斷的調(diào)用Looper.loopOnce()
,開(kāi)始真正的消息獲取和分發(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)過(guò)簡(jiǎn)化的代碼,首先調(diào)用MessageQueue.next()
從消息隊(duì)列中獲取消息,然后調(diào)用關(guān)鍵方法msg.target.dispatchMessage(msg)
開(kāi)始消息的分發(fā)執(zhí)行,這個(gè)方法之前的文章有進(jìn)行介紹,這里就不再過(guò)多介紹了。
接下來(lái)我們看下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); } } } //... } }
- 如果當(dāng)前消息隊(duì)列中沒(méi)有消息或者還沒(méi)到下一條消息的執(zhí)行時(shí)間,就調(diào)用
nativePollOnce()
方法休眠主線程,讓出CPU執(zhí)行權(quán); - 如果
Message
的target為null,就代表是一個(gè)消息屏障消息,之后就只能從消息隊(duì)列獲取異步消息了,如果不存在,就嘗試執(zhí)行Idle
消息; - 如果不存在消息屏障,則就從消息隊(duì)列中正常嘗試獲取
Message
,如果不存在,就嘗試執(zhí)行Idle
消息; - 執(zhí)行
Idle
消息,只有在主線程空閑(當(dāng)前消息隊(duì)列中沒(méi)有消息或者還沒(méi)到下一條消息的執(zhí)行時(shí)間)的情況下才會(huì)去嘗試執(zhí)行Idle
消息,這種類型的消息非常有用,具體的可以參考我之前寫(xiě)的文章:IdleHandler基本使用及應(yīng)用案例分析
總結(jié)
本篇文章主要是詳細(xì)分析了Android消息機(jī)制的整個(gè)執(zhí)行流程(不包括native層),最核心的就是Handler
、Looper
、MessageQueue
、Message
四個(gè)類及構(gòu)成的關(guān)聯(lián)。
到此這篇關(guān)于詳解Android消息機(jī)制完整的執(zhí)行流程的文章就介紹到這了,更多相關(guān)Android消息機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android多線程處理機(jī)制中的Handler使用介紹
本文將為大家介紹下Android的Handler的使用方法,Handler可以發(fā)送Messsage和Runnable對(duì)象到與其相關(guān)聯(lián)的線程的消息隊(duì)列,感興趣的朋友可以了解下哈2013-06-06Android實(shí)現(xiàn)錄音方法(仿微信語(yǔ)音、麥克風(fēng)錄音、發(fā)送語(yǔ)音、解決5.0以上BUG)
大家平時(shí)在使用微信qq聊天時(shí)經(jīng)常會(huì)發(fā)送語(yǔ)音功能,今天小編給大家?guī)?lái)了基于android實(shí)現(xiàn)錄音的方法仿微信語(yǔ)音、麥克風(fēng)錄音、發(fā)送語(yǔ)音、解決5.0以上BUG,需要的朋友參考下吧2018-04-04Android StatusBar 透明化方法(不同的版本適配)
本篇文章主要介紹了Android StatusBar 透明化方法(不同的版本適配),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Android種使用Notification實(shí)現(xiàn)通知管理以及自定義通知欄實(shí)例(示例四)
本篇文章主要介紹了Android種使用Notification實(shí)現(xiàn)通知管理以及自定義通知欄實(shí)例,具有一定的參考價(jià)值,需要的朋友可以了解一下。2016-12-12Android 日歷控件庫(kù),可左右滑動(dòng),顯示公歷,農(nóng)歷,節(jié)假日等功能
這篇文章主要介紹了Android 日歷控件庫(kù),可左右滑動(dòng),顯示公歷,農(nóng)歷,節(jié)假日等功能的相關(guān)資料,需要的朋友可以參考下2016-09-09android計(jì)時(shí)器,時(shí)間計(jì)算器的實(shí)現(xiàn)方法
android計(jì)時(shí)器,時(shí)間計(jì)算器的實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-03-03Android 側(cè)滑關(guān)閉Activity的實(shí)例
這篇文章主要介紹了Android 側(cè)滑關(guān)閉Activity的實(shí)例的相關(guān)資料,好的手機(jī)現(xiàn)在沒(méi)有物理返回鍵,或者說(shuō)統(tǒng)一Android 與IOS 軟件功能的時(shí)候,需要側(cè)滑關(guān)閉,需要的朋友可以參考下2017-07-07Android自定義控件實(shí)現(xiàn)底部菜單(下)
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)底部菜單的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01怎樣刪除android的gallery中的圖片實(shí)例說(shuō)明
長(zhǎng)按gallery中的圖片進(jìn)行刪除該圖片的操作,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06