詳解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)前主線程,開始從消息隊(duì)列獲取消息進(jìn)行執(zhí)行;
Looper.loop()分發(fā)消息
這個(gè)方法會(huì)開啟一個(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(),開始真正的消息獲取和分發(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)開始消息的分發(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消息,這種類型的消息非常有用,具體的可以參考我之前寫的文章: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獲取ROOT權(quán)限的實(shí)例代碼
這篇文章主要介紹了Android如何獲取ROOT權(quán)限,寫了一個(gè)小方法,大家可以在應(yīng)用中檢測(cè)ROOT權(quán)限2013-11-11
android 手機(jī)SD卡讀寫操作(以txt文本為例)實(shí)現(xiàn)步驟
要完成SD卡讀寫操作首先對(duì)manifest注冊(cè)SD卡讀寫權(quán)限其次是創(chuàng)建一個(gè)對(duì)SD卡中文件讀寫的類寫一個(gè)用于檢測(cè)讀寫功能的的布局然后就是UI的類了,感興趣的朋友可以參考下,希望可以幫助到你2013-02-02
Android控件BottomSheet實(shí)現(xiàn)底邊彈出選擇列表
這篇文章主要介紹了Android控件BottomSheet實(shí)現(xiàn)底邊彈出選擇列表,比較常用的選擇條件或跳轉(zhuǎn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android開發(fā)教程之ContentProvider數(shù)據(jù)存儲(chǔ)
這篇文章主要介紹了Android開發(fā)教程之ContentProvider數(shù)據(jù)存儲(chǔ)的相關(guān)資料,需要的朋友可以參考下2016-12-12
Android App中的GridView網(wǎng)格布局使用指南
GridView布局所實(shí)現(xiàn)的就是類似于九宮格的矩陣界面效果,下面整理了Android App中的GridView網(wǎng)格布局使用指南,包括分割線的添加與自定義GridView的實(shí)現(xiàn)等技巧,需要的朋友可以參考下2016-06-06
gradle配置國(guó)內(nèi)鏡像的實(shí)現(xiàn)
這篇文章主要介紹了gradle配置國(guó)內(nèi)鏡像的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
詳解Android studio實(shí)現(xiàn)語(yǔ)音轉(zhuǎn)文字功能
這篇文章主要介紹了如何通過(guò)Android studio調(diào)用科大訊飛的語(yǔ)音轉(zhuǎn)文字功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03

