前言
Android的消息機(jī)制用于同進(jìn)程的線程間通信瓣赂,它是由MessageQueue嗓化,Message撩鹿,Looper睡扬,Handler共同組成盟蚣,Android中大量的交互都是通過消息機(jī)制,比如四大組件啟動(dòng)過程與服務(wù)的交互卖怜、View的繪制屎开、更新等都離不開消息機(jī)制,所以Android在某種意義上也可以說成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng)马靠,在Android中消息機(jī)制的運(yùn)作分為java層和native層奄抽,它們之間的運(yùn)作機(jī)制不一樣,本文講解的是java層的消息機(jī)制甩鳄,如果你想了解native層的消息機(jī)制逞度,可以閱讀:
但其實(shí)java層的消息機(jī)制的核心功能都交給了native層的消息機(jī)制來完成,但作為應(yīng)用開發(fā)妙啃,首先要掌握java層的消息機(jī)制档泽。
本文源碼基于Android8.0烦粒,源碼相關(guān)位置:
frameworks/base/core/java/android/os/*.java (*代表MessageQueue谬擦、Handler荣堰、Looper酱塔、Message)
消息機(jī)制概述
Android應(yīng)用的每個(gè)事件都會(huì)轉(zhuǎn)化為一個(gè)系統(tǒng)消息即Message杉女,消息中包含了事件的相關(guān)信息和消息的處理人即Handler改鲫,消息要通過Handler發(fā)送仑撞,最終被投遞到一個(gè)消息隊(duì)列中即MessageQueue聋袋,它維護(hù)了一個(gè)待處理的消息列表突倍,然后通過Looper開啟了一個(gè)消息循環(huán)不斷地從這個(gè)隊(duì)列中取出消息腔稀,當(dāng)從消息隊(duì)列取出一個(gè)消息后,Looper根據(jù)消息的處理人(target)將此消息分發(fā)給相應(yīng)的Handle處理羽历,整個(gè)過程如下圖所示焊虏。
它們的工作原理就像工廠的生產(chǎn)線,Looper是發(fā)動(dòng)機(jī)秕磷,MessageQueue是傳送帶诵闭,Handler是工人,Message則是待處理的產(chǎn)品。
java層消息機(jī)制架構(gòu)圖
- Looper --- 是每個(gè)線程的MessageQueue管家疏尿,里面有一個(gè)MessageQueue消息隊(duì)列瘟芝,負(fù)責(zé)把消息從MessageQueue中取出并把消息傳遞到Handler中去,每個(gè)線程只有一個(gè)Looper褥琐;
- MessageQueue --- 消息隊(duì)列锌俱,有一組待處理的Message,主要用于存放所有通過Handler發(fā)送的消息敌呈,每個(gè)線程只有一個(gè)MessageQueue贸宏;
- Message --- 是線程之間傳遞的消息,里面有一個(gè)用于處理消息的Handler磕洪;
- Handler --- 主要用于發(fā)送和處理消息吭练,里面有Looper和MessageQueue。
簡(jiǎn)單使用
在開發(fā)中析显,我們?cè)谧泳€程中執(zhí)行完操作后通常需要更新UI鲫咽,但我們都知道不能在子線程中更新UI,此時(shí)我們就要通過Handler將一個(gè)消息post到UI線程中谷异,然后再在Handler中的handleMessage方法中進(jìn)行處理分尸,如果我們不傳遞UI線程所屬的Looper去創(chuàng)建Handler,那么該Handler必須在主線程中創(chuàng)建晰绎,如下:
//在主線程中創(chuàng)建Handler
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg){
//更新UI
}
}
//在子線程中進(jìn)行耗時(shí)操作
new Thread(){
public void run(){
mHandler.sendEmptyMessage(0);
}
}
以上就是我們平時(shí)使用Handler的常規(guī)用法了寓落。
接下來我們以應(yīng)用主線程(又叫做UI線程)的消息機(jī)制運(yùn)作為例講解Android消息機(jī)制的原理括丁。
源碼分析
1荞下、 Looper的創(chuàng)建
我們知道Android應(yīng)用程序的入口實(shí)際上是ActivityThread.main方法,而應(yīng)用的消息循環(huán)也是在這個(gè)方法中創(chuàng)建史飞,具體源碼如下:
//ActivityThread.java
public static void main(String[] args) {
//...
//1尖昏、創(chuàng)建消息循環(huán)Looper
Looper.prepareMainLooper();
//...
//2、執(zhí)行消息循環(huán)
Looper.loop();
}
我們關(guān)注注釋1构资,ActivityThread調(diào)用了Looper的prepareMainLooper方法來創(chuàng)建Looper實(shí)例抽诉,Looper的prepareMainLooper方法如下:
//Looper.java
public static void prepareMainLooper() {
//1、創(chuàng)建不允許退出的Looper
prepare(false);
synchronized (Looper.class) {//保證只有一個(gè)線程執(zhí)行
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//2吐绵、將剛剛創(chuàng)建的Looper實(shí)例賦值給sMainLooper
//sMainLooper字段是Looper類中專門為UI線程保留的迹淌,只要某個(gè)線程調(diào)用了prepareMainLooper方法,把它創(chuàng)建的Looper實(shí)例賦值給sMainLooper己单,它就可以成為UI線程唉窃,prepareMainLooper方法只允許執(zhí)行一次
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed){//quitAllowed表示是否允許Looper運(yùn)行時(shí)退出
//Looper.prepare()只能執(zhí)行一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//把Looper保存到TLS中
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
//獲取當(dāng)前線程TLS區(qū)域的Looper
return sThreadLocal.get();
}
private static Looper sMainLooper; // guarded by Looper.class
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
線程在調(diào)用prepareMainLooper方法后,就代表它成為了一個(gè)UI線程纹笼,可以看到prepareMainLooper方法中是調(diào)用Looper的prepare方法來創(chuàng)建Looper實(shí)例纹份,當(dāng)要獲取創(chuàng)建的Looper實(shí)例時(shí),是通過Looper的myLooper方法來獲取的,在調(diào)用prepare方法時(shí)蔓涧,會(huì)把創(chuàng)建的Looper實(shí)例set到ThreadLocal中件已,當(dāng)獲取時(shí)也會(huì)從ThreadLocal中通過get方法獲取,那么ThreadLocal是什么元暴?這里簡(jiǎn)單介紹一下ThreadLocal:
ThreadLocal:線程本地存儲(chǔ)區(qū)(Thread Local Storage篷扩,簡(jiǎn)稱為TLS),每 個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域茉盏,不同線程之間彼此不能訪問對(duì)方的TLS區(qū)域瞻惋。
它的常用操作方法有:
ThreadLocal.set(T value):將value存儲(chǔ)到當(dāng)前線程的TLS區(qū)域。
ThreadLocal.get():獲取當(dāng)前線程TLS區(qū)域的數(shù)據(jù)關(guān)于ThreadLocal更多信息可以查看ThreadLocal原理解析
ThreadLocal的get()和set()方法操作的類型都是泛型援岩,我們從ThreadLocal在Looper中的定義可以看出歼狼,sThreadLocal的get()和set()操作的類型都是Looper類型, 所以,由于ThreadLocal的作用享怀,每個(gè)線程只能保存一個(gè)Looper羽峰,不同線程的Looper是不相同的,這樣添瓷,通過調(diào)用Looper.prepare(false)方法梅屉,UI線程中就保存了它對(duì)應(yīng)的鳞贷、唯一的Looper實(shí)例坯汤,當(dāng)在UI線程調(diào)用Looper.myLooper方法時(shí)它就會(huì)返回UI線程關(guān)聯(lián)的Looper實(shí)例,當(dāng)然搀愧,如果你在子線程中想要獲得UI線程關(guān)聯(lián)的Looper實(shí)例惰聂,就需要調(diào)用getMainLooper方法,該方法如下:
public static Looper getMainLooper() {
synchronized (Looper.class) {
//返回UI線程的Looper實(shí)例
return sMainLooper;
}
}
以上就是UI線程的Looper的創(chuàng)建過程咱筛,執(zhí)行ActivityThread.main后搓幌,應(yīng)用程序就啟動(dòng)了,UI的消息循環(huán)也在Looper.loop方法中啟動(dòng)迅箩,此后Looper會(huì)一直從消息隊(duì)列中取出消息溉愁,用戶或系統(tǒng)通過Handler不斷往消息隊(duì)列中添加消息,這些消息不斷的被取出饲趋,處理拐揭,回收,使得應(yīng)用運(yùn)轉(zhuǎn)起來奕塑。
我們?cè)谄綍r(shí)開發(fā)時(shí)堂污,一般是使用不帶參數(shù)的prepare()方法來創(chuàng)建子線程對(duì)應(yīng)的Looper實(shí)例,如下:
//我們平時(shí)使用的prepare方法
public static void prepare() {
//quitAllowed = true
prepare(true);
}
和UI線程的區(qū)別是爵川,UI線程的Looper是不允許推出的敷鸦,而我們的Looper一般是允許退出的。
2、MessageQueue的創(chuàng)建
我們?cè)谏厦嬷腊桥{(diào)用Looper的prepare方法就會(huì)創(chuàng)建Looper實(shí)例值依,同時(shí)會(huì)把Looper實(shí)例通過ThreadLocal保存到線程中,在創(chuàng)建Looper時(shí)還會(huì)同時(shí)在構(gòu)造中創(chuàng)建MessageQueue實(shí)例碟案,如下:
//Looper.java
final MessageQueue mQueue;
//Looper唯一的構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
//可以看到MessageQueue是在Looper中創(chuàng)建的
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper只有這一個(gè)構(gòu)造函數(shù)愿险,并且它是私有的,所以我們只能通過Looper的prepare方法創(chuàng)建Looper實(shí)例价说,由于ThreadLocal辆亏,每個(gè)線程最多只能對(duì)應(yīng)一個(gè)Looper實(shí)例,而每個(gè)Looper內(nèi)部只有一個(gè)MessageQueue實(shí)例鳖目,推出:每個(gè)線程最多對(duì)應(yīng)一個(gè)MessageQueue實(shí)例扮叨。
我們看一下MessageQueue的構(gòu)造,如下:
//MessageQueue.java
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通過native方法初始化消息隊(duì)列领迈,其中mPtr是供native代碼使用
mPtr = nativeInit();
}
MessageQueue的構(gòu)造中會(huì)通過native方法在native層創(chuàng)建一個(gè)屬于native層的消息隊(duì)列NativeMessageQueue彻磁,然后把NativeMessageQueue的地址返回給java層保存在mPtr中,java層和native層之間的通信就通過這個(gè)mPtr指針狸捅。
MessageQueue是消息機(jī)制的核心類衷蜓,它是java層和native層的連接紐帶,它里面有大量的native方法尘喝,Android有倆套消息機(jī)制(java層和native層磁浇,實(shí)現(xiàn)不一樣),但本文只講解java層的消息機(jī)制朽褪,不會(huì)涉及到native層.
關(guān)于native層的查看Android消息機(jī)制(native層)
我們通過Looper的myQueue方法就能獲取到它關(guān)聯(lián)的MessageQueue實(shí)例置吓,如下:
//Looper.java
public static @NonNull MessageQueue myQueue() {
//先通過TLS獲取Looper實(shí)例,再從對(duì)應(yīng)的Looper中獲取MessageQueue實(shí)例
return myLooper().mQueue;
}
3鞍匾、消息循環(huán)的運(yùn)行
在ActivityThread的mian方法在創(chuàng)建Looper后交洗,通過Looper.loop方法就啟動(dòng)了消息循環(huán),這個(gè)函數(shù)會(huì)不斷的從MessageQueue中取出消息橡淑、處理消息,我們點(diǎn)進(jìn)此方法看一下它的源碼:
//Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//從Looper中取出MessageQueue
final MessageQueue queue = me.mQueue;
//...
for (;;) {
//1咆爽、從MessageQueue中取出消息梁棠,沒有消息時(shí)會(huì)阻塞等待
Message msg = queue.next();
//next()返回了null,表示MessageQueue正在退出斗埂,即調(diào)用了Looper的quit或quitSafely方法
if (msg == null) {
return;
}
//...
//2符糊、分發(fā)消息
msg.target.dispatchMessage(msg);
//...
//3、回收消息
msg.recycleUnchecked();
}
}
loop()中是一個(gè)死循環(huán)呛凶,我們關(guān)注注釋1男娄,loop()會(huì)調(diào)用MessageQueue的next()來獲取最新的消息,當(dāng)沒有消息時(shí),next()會(huì)一直阻塞在那里模闲,這也導(dǎo)致loop()阻塞建瘫,唯一跳出循環(huán)的條件是next()返回null,這時(shí)代表Looper的quit()或quitSafely()被調(diào)用尸折,從而調(diào)用MessageQueue的quit()來通知消息隊(duì)列退出啰脚,如下:
//Looper.java
public void quit() {
mQueue.quit(false);
}
//quitSafely和quit方法的區(qū)別是,quitSafely方法會(huì)等MessageQueue中所有的消息處理完后才退出实夹,而quit會(huì)直接退出
public void quitSafely() {
mQueue.quit(true);
}
所以MessageQueue的next()是最關(guān)鍵的函數(shù)橄浓,我們來看看next函數(shù)的關(guān)鍵代碼:
//MessageQueue.java
Message next() {
//mPtr是在構(gòu)造中被賦值,是指向native層的MessageQueue
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
//一個(gè)死循環(huán)亮航,里面分為兩部分荸实,1、處理java層消息缴淋;2泪勒、如果沒有消息處理,執(zhí)行IdleHandler
for(;;){
//...
//Part1:獲取java層的消息處理
//nativePollOnce方法用于處理native層消息宴猾,是一個(gè)阻塞操作
//它在這兩種情況下返回:1圆存、等待nextPollTimeoutMillis時(shí)長(zhǎng)后;2仇哆、MessageQueue被喚醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//mMessages是java層的消息隊(duì)列沦辙,這里表示取出消息隊(duì)列的第一個(gè)消息
Message msg = mMessages;
if (msg != null && msg.target == null) {//遇到同步屏障(target為null的消息)
//在do-while中找到異步消息,優(yōu)先處理異步消息
//異步消息的isAsynchronous方法返回true
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {//有消息
if (now < msg.when) { //消息還沒到觸發(fā)時(shí)間
//設(shè)置下一次輪詢的超時(shí)時(shí)長(zhǎng)(等待時(shí)長(zhǎng))
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {//now == msg.when, 消息到達(dá)觸發(fā)時(shí)間
mBlocked = false;
//從mMessages的頭部獲取一條消息并返回
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//設(shè)置消息的使用狀態(tài)讹剔,即flags |= FLAG_IN_US
msg.markInUse();
//返回這個(gè)消息
return msg;
}
} else {//msg == null油讯,沒有消息
//設(shè)置nextPollTimeoutMillis為-1,準(zhǔn)備進(jìn)入阻塞延欠,等待MessageQueue被喚醒
nextPollTimeoutMillis = -1;
}
//調(diào)用了quit方法
if (mQuitting) {
dispose();
return null;
}
//當(dāng)處于以下2種情況時(shí)陌兑,就會(huì)執(zhí)行到Part2:
//1、mMessages == null由捎,java層沒有消息處理
//2兔综、now < msg.when,有消息處理狞玛,但是還沒有到消息的執(zhí)行時(shí)間
//1软驰、2兩種情況都表明線程的消息隊(duì)列處于空閑狀態(tài),處于空閑狀態(tài)心肪,就會(huì)執(zhí)行IdleHandler
//Part2:沒有消息處理锭亏,執(zhí)行IdleHandler
//mIdleHandlers表示IdleHandler列表
//pendingIdleHandlerCount表示需要執(zhí)行的IdleHandler的數(shù)量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//沒有IdleHandler需要處理,可直接進(jìn)入阻塞
mBlocked = true;
continue;
}
//有IdleHandler需要處理
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//把mIdleHandlers列表轉(zhuǎn)成mPendingIdleHandlers數(shù)組
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//遍歷mPendingIdleHandlers數(shù)組
for (int i = 0; i < pendingIdleHandlerCount; i++) {
//取出IdleHandler
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//執(zhí)行IdleHandler的queueIdle方法硬鞍,通過返回值由自己決定是否保持存活狀態(tài)
keep = idler.queueIdle();
}
//...省略異常處理
if (!keep) {
// 不需要存活慧瘤,移除
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置pendingIdleHandlerCount和nextPollTimeoutMillis為0
pendingIdleHandlerCount = 0;
//nextPollTimeoutMillis為0戴已,表示下一次循環(huán)會(huì)馬上從Messages中取出一個(gè)Message判斷是否到達(dá)執(zhí)行時(shí)間
//因?yàn)镮dleHandler在處理事件的時(shí)間里,有可能有新的消息發(fā)送來過來锅减,需要重新檢查
nextPollTimeoutMillis = 0;
}
}
MessageQueue的next方法有點(diǎn)長(zhǎng)糖儡,但是它里面的邏輯是很好理解的,它主要是通過一個(gè)死循環(huán)不斷的返回Message給Looper處理上煤,next方法可以分為兩部分閱讀:
1休玩、獲取java層的消息處理:
我們看Part1,先執(zhí)行nativePollOnce方法劫狠,它是一個(gè)阻塞操作拴疤,其中nextPollTimeoutMillis代表下一次等待的超時(shí)時(shí)長(zhǎng),當(dāng)nextPollTimeoutMillis = 0時(shí)或到達(dá)nextPollTimeoutMillis時(shí)独泞,它會(huì)立即返回呐矾;當(dāng)nextPollTimeoutMillis = -1時(shí),表示MessageQueue中沒有消息懦砂,會(huì)一直等待下去蜒犯,直到Hanlder往消息隊(duì)列投遞消息,執(zhí)行nativeWake方法后荞膘,MessageQueue被喚醒罚随,nativePollOnce就會(huì)返回,但它此時(shí)并不是立即返回羽资,它會(huì)先處理完native層的消息后淘菩,再返回,然后獲取java層的消息處理屠升;
接著next方法就會(huì)從mMessages鏈表的表頭中獲取一個(gè)消息潮改,首先判斷它是否是同步屏障,同步屏障就是target為null的Message腹暖,如果遇到同步屏障汇在,MessageQueue就會(huì)優(yōu)先獲取異步消息處理,異步消息就是優(yōu)先級(jí)比同步消息高的消息脏答,我們平時(shí)發(fā)送的就是同步消息糕殉,通過Message的setAsynchronous(true)可以把同步消息變成異步消息,不管是同步還是異步以蕴,都是Message糙麦,獲取到Message后;
接著判斷Message是否到達(dá)它的執(zhí)行時(shí)間(if(now == msg.when))丛肮,如果到達(dá)了執(zhí)行時(shí)間,next方法就會(huì)返回這條消息給Looper處理魄缚,并將其從單鏈表中刪除宝与;如果還沒有到達(dá)執(zhí)行時(shí)間焚廊,就設(shè)置nextPollTimeoutMillis為下一次等待超時(shí)時(shí)長(zhǎng),等待下次再次取出判斷习劫,可以發(fā)現(xiàn)雖然MessageQueue叫消息隊(duì)列咆瘟,但它卻不是用隊(duì)列實(shí)現(xiàn)的,而是用鏈表實(shí)現(xiàn)的诽里。
通過MessageQueue的postSyncBarrier方法可以添加一個(gè)同步屏障袒餐,通過removeSyncBarrier方法可以移除相應(yīng)的同步屏障,在Android谤狡,Choreographer機(jī)制中就使用到了異步消息灸眼,在View樹繪制之前,會(huì)先往UI線程的MessageQueue添加一個(gè)同步屏障墓懂,攔截同步消息焰宣,然后發(fā)送一個(gè)異步消息,等待VSYN信號(hào)到來捕仔,觸發(fā)View樹繪制匕积,這樣就可以讓繪制任務(wù)優(yōu)先執(zhí)行。
2榜跌、沒有消息處理闪唆,遍歷IdleHandler列表,執(zhí)行IdleHandler的queueIdle方法:
IdleHandler是什么钓葫?IdleHandler是一個(gè)接口悄蕾,它里面只有一個(gè)queueIdle方法,Idle是空閑的意思瓤逼,在MessageQueue空閑的時(shí)候會(huì)執(zhí)行IdleHandler的queueIdle方法笼吟,我們可以通過MessageQueue的addIdleHandler方法添加我們自定義的IdleHandler到mIdleHandlers列表中,如下:
//MessageQueue.java
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
public void addIdleHandler(@NonNull IdleHandler handler) {
//...
mIdleHandlers.add(handler);
}
MessageQueue空閑霸旗,就代表它處于以下兩種情況:
(1) mMessages == null贷帮,消息隊(duì)列中沒有消息處理;
(2) now < msg.when诱告,有消息處理撵枢,但是還沒有到消息的執(zhí)行時(shí)間.
以上兩種情況之一都會(huì)觸發(fā)next方法遍歷IdleHandler列表,執(zhí)行IdleHandler的queueIdle方法的操作精居。
在Android中锄禽,我們平時(shí)所說的線程空閑其實(shí)就是指線程的MessageQueue空閑,這時(shí)就可以執(zhí)行我們添加的IdleHandler靴姿,例如在LeakCanary中沃但,它通過添加IdleHandler,在UI線程空閑時(shí)執(zhí)行內(nèi)存泄漏的判斷邏輯.
4佛吓、消息的發(fā)送
到這里UI線程已經(jīng)啟動(dòng)了消息循環(huán)宵晚,那么消息從何而來垂攘?消息是由系統(tǒng)產(chǎn)生,然后通過Hanlder發(fā)送到MessageQueue中淤刃,Handler就是用來處理和發(fā)送消息的晒他,應(yīng)用程序的Handler在ActivityThread中被創(chuàng)建,如下:
//ActivityThread.java
final H mH = new H();
//H定義如下逸贾,繼承Hanldler
//里面定義了大量的字段陨仅,跟Activity的啟動(dòng),Application的綁定等有關(guān)
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int CREATE_SERVICE = 114;
//....
public void handleMessage(Message msg) {
switch (msg.what) {
case BIND_APPLICATION:
//...
case EXIT_APPLICATION:
//...
case CREATE_SERVICE:
//...
//...
}
//...
}
mH就是應(yīng)用程序內(nèi)部使用的Handler铝侵,我們外部是不可使用的灼伤,應(yīng)用程序通過Handler往MessageQueue中投遞消息,并通過Handler的handlerMessage方法處理消息哟沫,而我們?cè)谕獠恳部梢允褂米约簞?chuàng)建的Handler往UI線程的MessageQueue投遞消息饺蔑。
既然Handle可以往MessageQueue中投遞消息,這說明Handler要和相應(yīng)的MessageQueue關(guān)聯(lián)嗜诀,我們看Handler的構(gòu)造函數(shù)猾警,Handler有兩種構(gòu)造函數(shù):
一種是指定Callback的構(gòu)造函數(shù),Callback默認(rèn)為null隆敢,如下:
//Handler.java
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
//...
//先通過TLS獲取Looper實(shí)例发皿,與Looper關(guān)聯(lián)
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//通過Looper獲取MessageQueue,與MessageQueue關(guān)聯(lián)
mQueue = mLooper.mQueue;
//Callback的作用在消息的分發(fā)中會(huì)講到
mCallback = callback;
mAsynchronous = async;
}
在構(gòu)造中如果通過Looper.myLooper方法獲取不到Looper拂蝎,就會(huì)拋出“Can't create handler inside threadxx that has not called Looper.prepare()”異常穴墅,所以如果我們?cè)谧泳€程中使用Handler的默認(rèn)構(gòu)造,沒有先調(diào)用Looper.prepare方法就創(chuàng)建Handler的話温自,就會(huì)拋出上述異常玄货,但是在UI線程中就不會(huì),因?yàn)閼?yīng)用程序啟動(dòng)時(shí)就已經(jīng)調(diào)用了Looper的prepareMainLooper方法悼泌,在該方法里面已經(jīng)調(diào)用了prepare(false)方法創(chuàng)建了UI線程的Looper實(shí)例松捉,無需我們?cè)俅握{(diào)用Looper.prepare方法。
另外一種是指定Looper的構(gòu)造函數(shù)(如果不指定馆里,Looper默認(rèn)從當(dāng)前線程的TLS區(qū)域獲取)隘世,如下:
//Handler.java
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
//與Looper關(guān)聯(lián)
mLooper = looper;
//通過Looper獲取MessageQueue,與MessageQueue關(guān)聯(lián)
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
不管哪一種構(gòu)造函數(shù)鸠踪,可以看到丙者,在Handler的最終構(gòu)造中都會(huì)和對(duì)應(yīng)線程的Looper、MessageQueue關(guān)聯(lián)营密,所以就算Hander在子線程創(chuàng)建械媒,我們也可以通過: Handler = new Handler(Looper.getMainLooper);把Handler關(guān)聯(lián)上UI線程的Looper评汰,并通過Looper關(guān)聯(lián)上UI線程的MessageQueue滥沫,這樣侣集,就能把Handler運(yùn)行在UI線程中键俱。
消息的發(fā)送可以通過handler的一系列post方法和一系列的send方法兰绣,一系列post方法最終通過一系列send方法來實(shí)現(xiàn),一系列send方法最終通過enqueueMessage方法來發(fā)送消息编振,如下:
可以發(fā)現(xiàn)Handler所有發(fā)送消息的方法缀辩,最終都是調(diào)用Handler的enqueueMessag方法,該方法源碼如下:
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在該方法中踪央,首先把Message的target字段設(shè)置為當(dāng)前發(fā)送消息的Handler, 然后設(shè)置Message是否是異步消息臀玄,最后把所有邏輯交給MessageQueue的enqueueMessage方法,該方法的相應(yīng)源碼如下:
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//...
synchronized (this) {
//正在退出時(shí)畅蹂,回收msg健无,加入到消息池
if (mQuitting) {
//...
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
boolean needWake;
//取mMessages鏈表頭部的消息
Message p = mMessages;
//滿足以下2種情況之一就把msg插入到鏈表頭部:
//1、如果p為null液斜,則代表mMessages沒有消息
//2累贤、如果when == 0 或 when < p.when, 則代表msg的觸發(fā)時(shí)間是鏈表中最早的
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;//如果處于阻塞狀態(tài),需要喚醒
} else {//3少漆、如果p != null且msg并不是最早觸發(fā)的臼膏,就在鏈表中找一個(gè)位置把msg插進(jìn)去
//如果處于阻塞狀態(tài),并且鏈表頭部是一個(gè)同步屏障示损,并且插入消息是最早的異步消息渗磅,需要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//下面是一個(gè)鏈表的插入操作, 將消息按時(shí)間順序插入到mMessages中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//如果在找到插入位置之前检访,發(fā)現(xiàn)了異步消息的存在始鱼,不需要喚醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
//nativeWake方法會(huì)喚醒當(dāng)前線程的MessageQueue
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue的enqueueMessage方法主要是一個(gè)鏈表的插入操作,返回true就代表插入成功脆贵,返回false就代表插入失敗医清,它主要分為以下3種情況:
1、插入鏈表頭部:
mMessages是按照Message觸發(fā)時(shí)間的先后順序排列的丹禀,越早觸發(fā)的排得越前状勤,頭部的消息是將要最早觸發(fā)的消息,當(dāng)有消息需要加入mMessages時(shí)双泪,如果mMessages為空或這個(gè)消息是最早觸發(fā)的持搜,就會(huì)直接插入鏈表頭部;
2焙矛、插入鏈表的中間位置:
如果消息鏈表不為空并且插入的消息不是最早觸發(fā)的葫盼,就會(huì)從鏈表頭部開始遍歷,直到找到消息應(yīng)該插入的合適位置村斟,以保證所有消息的時(shí)間順序贫导,這一個(gè)過程可以理解為插入排序抛猫;
3、判斷是否調(diào)用nativeWake方法
最后孩灯,根據(jù)needWake是否為true闺金,來決定是否調(diào)用nativeWake方法喚醒當(dāng)前線程的MessageQueue,needWake默認(rèn)為false峰档,即不需要喚醒败匹,needWake為true就代表此時(shí)處于以下2種情況:
(1)如果插入消息在鏈表頭部并且mBlocked == true,表示此時(shí)nativePollOnce方法進(jìn)入阻塞狀態(tài)讥巡,等待被喚醒返回掀亩;
(2)如果插入消息在鏈表中間,消息鏈表的頭部是一個(gè)同步屏障欢顷,同時(shí)插入的消息是鏈表中最早的異步消息槽棍,需要喚醒,即時(shí)處理異步消息抬驴。
5炼七、消息的分發(fā)
在消息循環(huán)的運(yùn)行中,如果loop方法中MessageQueue的next方法返回了Message怎爵,那么就會(huì)執(zhí)行到這一句:msg.target.dispatchMessage(msg)特石;Looper會(huì)把這條消息交給該Message的target(Handler對(duì)象)來處理, 實(shí)際上是轉(zhuǎn)了一圈,Handler把消息發(fā)送給MessageQueue鳖链,Looper又把這個(gè)消息給Handler處理姆蘸,下面來看消息分發(fā)邏輯,dispatchMessage()源碼如下:
//Handler.java
public void dispatchMessage(Message msg) {
//1芙委、檢查msg的callback是否為空
if (msg.callback != null) {
handleCallback(msg);
} else {
//2逞敷、Handler的mCallback是否為空
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//3、我們平常處理消息的方法灌侣,該方法默認(rèn)為空推捐,Handler子類通過覆寫該方法來完成具體的邏輯。
handleMessage(msg);
}
}
里面代碼量很少侧啼,分為3步:
1牛柒、檢查msg.callback是否為空,不為空則執(zhí)行" handleCallback(msg)", 源碼如下:
//Handler.java
private static void handleCallback(Message message) {
message.callback.run();
}
msg.callback其實(shí)是一個(gè)Runnable對(duì)象痊乾,當(dāng)我們通過Handler來post一個(gè)Runnable消息時(shí)皮壁,它就不為空,如下:
//Handler.java
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
//把Runnable對(duì)象包裝成Message對(duì)象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到哪审,在post(Runnable r)中蛾魄,會(huì)把Runnable包裝成Message對(duì)象,并把Runnable設(shè)置給Message的callback字段,然后發(fā)送此消息滴须。
2舌狗、如果msg.callback為空,檢查mCallback是否為空扔水,mCallback是一個(gè)Callback接口痛侍,定義如下:
//Handler.java
public interface Callback {
public boolean handleMessage(Message msg);
}
當(dāng)我們這樣:Handler handler = new Handler(callback) 來創(chuàng)建Handler時(shí), mCallback就不為空,它的意義是當(dāng)我們不想派生Handler的子類重寫handleMessage()來處理消息時(shí)铭污,就可以通過Callback來實(shí)現(xiàn)恋日。
3、如果mCallback為空嘹狞,最后調(diào)用Handler的handleMessage方法來處理消息,這就是我們平時(shí)熟悉的處理消息的方法誓竿。
從1磅网、2、3可以看出筷屡,在Handler中涧偷,處理消息的回調(diào)的優(yōu)先級(jí)為:Message的Callback > Handler的Callback > Handler的handleMessage方法。
6毙死、消息的回收復(fù)用
1燎潮、消息的復(fù)用
前面多次提到了Message,當(dāng)我們通過Handler的obtainMessage()或Message的obtain()獲取一個(gè)Message對(duì)象時(shí)扼倘,系統(tǒng)并不是每次都new一個(gè)出來确封,而是先從消息池中(sPool)嘗試獲取一個(gè)Message。Handler的obtainMessage()最終是調(diào)用了Message的obtain()再菊,Message的obtain方法如下:
//Message.java
public static Message obtain() {
synchronized (sPoolSync) {
//從sPool頭部取出一個(gè)Message對(duì)象返回爪喘,并把消息從鏈表斷開(即把sPool指向下一個(gè)Message)
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;//清除in-use flag
sPoolSize--;//消息池的大小進(jìn)行減1操作
return m;
}
}
//消息池中沒有Message,直接new一個(gè)返回
return new Message();
sPool的數(shù)據(jù)類型為Message纠拔,通過next成員變量秉剑,維護(hù)一個(gè)消息池,消息池的默認(rèn)大小為50稠诲。定義如下:
public final class Message implements Parcelable {
public static final Object sPoolSync = new Object();//用于在獲取Message對(duì)象時(shí)進(jìn)行同步鎖
private static int sPoolSize = 0;//池的大小
private static final int MAX_POOL_SIZE = 50;//池的可用大小
private static Message sPool;
Message next;
//...
}
雖然叫消息池侦鹏,其實(shí)是通過鏈表實(shí)現(xiàn)的,每個(gè)Message都有一個(gè)同類型的next字段臀叙,這個(gè)next就是指向下一個(gè)可用的Message略水,最后一個(gè)可用的Message的next為空,這樣所有可用的Message對(duì)象就通過next串成一個(gè)Message池匹耕,sPool指向池中的第一個(gè)Message聚请,復(fù)用消息其實(shí)是從鏈表的頭部獲取一個(gè)Message返回。
2、消息的回收
我們發(fā)現(xiàn)在obtain方法中新創(chuàng)建Message對(duì)象時(shí)驶赏,并不會(huì)直接把它放到池中再返回炸卑,那么Message對(duì)象是什么時(shí)候被放進(jìn)消息池中的呢?是在回收Message時(shí)把它放入池中煤傍,Message中也有類似Bitmap那樣的recycler函數(shù)盖文,如下:
//Message.java
public void recycle() {
//判斷消息是否正在使用
if (isInUse()) {
if (gCheckRecycle) {//Android 5.0以后的版本默認(rèn)為true,之前的版本默認(rèn)為false.
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
//對(duì)于不再使用的消息,加入到消息池
void recycleUnchecked() {
//將消息標(biāo)示位置為IN_USE蚯姆,并清空消息所有的參數(shù)
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//當(dāng)消息池沒有滿時(shí)五续,將Message對(duì)象加入消息池(即把Message插入鏈表頭部)
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;//消息池的可用大小進(jìn)行加1操作
}
}
}
recycler函數(shù)先判斷該Message是否還在使用,如果還在使用龄恋,就會(huì)拋異常疙驾,否則就調(diào)用recyclerUnchecked函數(shù)根據(jù)MAX_POOL_SIZE判斷是否把該消息回收,回收前還要先清空該消息的各個(gè)字段郭毕,回收消息就是把自身插入到鏈表的表頭它碎。
通過消息的復(fù)用回收,減少M(fèi)essage對(duì)象不斷創(chuàng)建與銷毀的過程显押,提升了效率扳肛。
7、小結(jié)
1乘碑、創(chuàng)建Looper時(shí)挖息,只能通過Looper的prepare方法創(chuàng)建,在創(chuàng)建Looper時(shí)會(huì)在內(nèi)部創(chuàng)建一個(gè)MessageQueue兽肤,并把Looper保存在線程的TLS區(qū)域中套腹,一個(gè)線程只能對(duì)應(yīng)一個(gè)Looper,一個(gè)Looper只能對(duì)應(yīng)一個(gè)MessageQueue轿衔;
2沉迹、在創(chuàng)建MessageQueue時(shí),MessageQueue與NativeMessageQueue建立連接害驹,NativeMessageQueue存儲(chǔ)地址存于MessageQueue的mPtr字段中鞭呕,java層和native通過mPtr字段進(jìn)行通信(native端通過Linux的epoll機(jī)制建立起消息機(jī)制);
3、由于ThreadLocal的作用宛官,Looper屬于某個(gè)線程葫松,而MessageQueue存儲(chǔ)在Looper中,所以MessageQueue則通過Looper與特定的線程關(guān)聯(lián)上底洗,而Handler在構(gòu)造中又與Looper和MessageQueue相關(guān)聯(lián)腋么,當(dāng)我們通過Handler發(fā)送消息時(shí),消息就會(huì)被插入到Handler關(guān)聯(lián)的MessageQueue中亥揖,而Looper會(huì)不斷的輪詢消息珊擂,從MessageQueue中取出消息給相應(yīng)的Handler處理圣勒,所以最終通過Handler發(fā)送的消息就會(huì)被執(zhí)行到Looper所在線程上,這就是Handler線程切換的原理摧扇,無論發(fā)送消息的Handler對(duì)象處于什么線程圣贸,最終處理消息的都是Looper所在線程;
4扛稽、Looper從MessageQueue中取出消息后吁峻,會(huì)交給消息的target(Handler)處理,在Handler中在张,處理消息的回調(diào)的優(yōu)先級(jí)為:Message的Callback > Handler的Callback > Handler的handleMessage方法用含;
5、因?yàn)閼?yīng)用程序啟動(dòng)時(shí)在ActivityThread.main方法中的Looper.prepareMainLooper()中已經(jīng)調(diào)用了Looper.prepare(false),所以在主線程中創(chuàng)建Handler無需我們手動(dòng)調(diào)用Looper.prepare()帮匾,而在子線程中啄骇,如果我們不傳遞UI線程所屬的Looper去創(chuàng)建Handler,那么就需要調(diào)用Looper.prepare()后再創(chuàng)建Handle來傳遞消息辟狈,因?yàn)镠andler要和某個(gè)線程中的MessageQueue和Looper關(guān)聯(lián)肠缔,只有調(diào)用Looper.prepare方法,Looper和MessageQueue才屬于某個(gè)線程哼转;
6、消息池是一個(gè)單鏈表槽华,復(fù)用Message時(shí)壹蔓,從頭出取出,如果取不到猫态,則新建返回佣蓉,回收Message時(shí),也從頭插入亲雪。
結(jié)語
本文從Android應(yīng)用UI線程消息循環(huán)的創(chuàng)建勇凭,消息循環(huán)的啟動(dòng),Handler與Looper义辕、MessageQueue的關(guān)聯(lián)虾标,消息的發(fā)送與分發(fā),還有消息的復(fù)用這幾個(gè)角度來講解了Message灌砖,Handler璧函,MessageQueue,Looper之間是如何配合工作基显,在你了解java層的消息機(jī)制是如何運(yùn)作后蘸吓,希望大家去了解一下native的消息機(jī)制诸衔,例如要想知道為什么loop方法是死循環(huán)但卻不會(huì)消耗性能赶么,這些只有native層的消息機(jī)制才能給你答案.
除此之外,我們還知道了MessageQueue的IdleHandler的作用鹃锈,它會(huì)在線程空閑時(shí)工作,還有異步消息的處理宪萄,它的優(yōu)先級(jí)高于同步消息艺谆,會(huì)被優(yōu)先處理,還有它們的應(yīng)用場(chǎng)景雨膨,一般我們是在子線程切換到UI線程時(shí)使用Handler機(jī)制擂涛,但其實(shí)我們也可以在子線程使用Handler機(jī)制,可以參考Android中的HandlerThread聊记,它的底層就是Handler+Thread.
以上就是本文的全部?jī)?nèi)容撒妈,希望大家有所收獲。
參考資料: