網(wǎng)站首頁 編程語言 正文
從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層),最核心的就是Handler
、Looper
、MessageQueue
、Message
四個類及構成的關聯(lián)。
原文鏈接:https://juejin.cn/post/7152667953161322510
相關推薦
- 2022-02-27 select組件選中后獲取當前值對應的對象信息
- 2022-04-09 Android項目中gradle的執(zhí)行流程_Android
- 2022-09-24 Go?iota關鍵字與枚舉類型實現(xiàn)原理_Golang
- 2022-09-02 R語言which函數(shù)介紹及Rcpp改寫詳解_R語言
- 2022-07-07 Python數(shù)據(jù)分析之?Matplotlib?散點圖繪制_python
- 2022-12-23 C++成員函數(shù)如何當作回調(diào)函數(shù)同時傳遞this指針_C 語言
- 2022-12-06 python中isdigit()?isalpha()用于判斷字符串的類型問題_python
- 2022-08-14 Hyper-V設置虛擬機固定Ip的方法步驟_Hyper-V
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支