Android 消息處理機(jī)制估計(jì)都被寫爛了,但是依然還是要寫一下,因?yàn)锳ndroid應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的,Android某種意義上也可以說(shuō)成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng),UI、事件、生命周期都和消息處理機(jī)制息息相關(guān),并且消息處理機(jī)制在整個(gè)Android知識(shí)體系中也是尤其重要,在太多的源碼分析的文章講得比較繁瑣,很多人對(duì)整個(gè)消息處理機(jī)制依然是懵懵懂懂,這篇文章通過(guò)一些問(wèn)答的模式結(jié)合Android主線程(UI線程)的工作原理來(lái)講解,源碼注釋很全,還有結(jié)合流程圖,如果你對(duì)Android 消息處理機(jī)制還不是很理解,我相信只要你靜下心來(lái)耐心的看,肯定會(huì)有不少的收獲的。
概述#
1、我們先說(shuō)下什么是Android消息處理機(jī)制?
消息處理機(jī)制本質(zhì):一個(gè)線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理其他線程給它發(fā)的消息。
簡(jiǎn)單的說(shuō):一個(gè)線程開啟一個(gè)無(wú)限循環(huán)模式,不斷遍歷自己的消息列表,如果有消息就挨個(gè)拿出來(lái)做處理,如果列表沒(méi)消息,自己就堵塞(相當(dāng)于wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什么事,就往該線程的消息隊(duì)列插入消息,該線程會(huì)不斷從隊(duì)列里拿出消息做處理。
2、Android消息處理機(jī)制的工作原理?
打個(gè)比方:公司類比App
- PM 的主要工作是設(shè)計(jì)產(chǎn)品,寫需求文檔,改需求,中途改需求,提測(cè)前改需求...
- UI 主要工作是UI設(shè)計(jì),交互等。
- RD 工作我就不說(shuō)了
- CEO 不解釋。
公司開創(chuàng)之后(App啟動(dòng)),那么CEO開始干活了(主線程【UI線程】啟動(dòng)),這時(shí)候CEO開啟了無(wú)限循環(huán)工作狂模式,自己的公司沒(méi)辦法?。ㄏ喈?dāng)于UI主線程轉(zhuǎn)成Looper線程【源碼里面有】)CEO招了一名RD(new Handler 實(shí)例)并把告訴PM和UI,如果你們有什么任務(wù)和需求就讓RD(Handler實(shí)例)轉(zhuǎn)告給我(CEO)。RD會(huì)把PM和UI的需求(Message)一條條記到CEO的備忘錄里(MessageQueue)。CEO 無(wú)限循環(huán)的工作就是不斷查看備忘錄,看有什么任務(wù)要做,有任務(wù)就從備忘錄一條一條拿出任務(wù)來(lái),然后交給這一名RD(Handler 實(shí)例)去處理(畢竟CEO 不會(huì)寫代碼 囧...)。當(dāng)然如果備忘錄都做完了,這時(shí)候CEO就會(huì)去睡覺(jué)(線程堵塞【簡(jiǎn)單理解成線程wait】,讓出CPU資源,讓其他線程去執(zhí)行)。但是這個(gè)備忘錄有個(gè)特殊的功能就是沒(méi)有任務(wù)的時(shí)候突然插入第一條任務(wù)(從無(wú)到有)就會(huì)有鬧鐘功能叫醒CEO起床繼續(xù)處理備忘錄。 整個(gè)消息處理機(jī)制的工作原理基本就是這樣的。后面會(huì)有源碼分析,你再來(lái)結(jié)合這個(gè)場(chǎng)景,會(huì)更好理解一些。
這里先給一張Android消息處理機(jī)制流程圖和具體執(zhí)行動(dòng)畫,如果看不懂沒(méi)事,接著往下看(后面會(huì)結(jié)合Android UI主線程來(lái)講解),然后結(jié)合著圖和動(dòng)畫一塊看更能理解整個(gè)機(jī)制的實(shí)現(xiàn)原理。


3、Looper、Handler、MessageQueue,Message作用和存在的意義?
**Looper **
我們知道一個(gè)線程是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線程生命周期便會(huì)終止,線程就會(huì)退出,那么做為App的主線程,如果代碼段執(zhí)行完了會(huì)怎樣?,那么就會(huì)出現(xiàn)App啟動(dòng)后執(zhí)行一段代碼后就自動(dòng)退出了,這是很不合理的。所以為了防止代碼段被執(zhí)行完,只能在代碼中插入一個(gè)死循環(huán),那么代碼就不會(huì)被執(zhí)行完,然后自動(dòng)退出,怎么在在代碼中插入一個(gè)死循環(huán)呢?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會(huì)變當(dāng)前線程變成Looper線程(可以先簡(jiǎn)單理解:無(wú)限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼,所以主線程會(huì)進(jìn)入while(true){...}的代碼段跳不出來(lái),但是主線程也不能什么都不做吧?其實(shí)所有做的事情都在while(true){...}里面做了,主線程會(huì)在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動(dòng),生命周期,更新UI,控件事件等),一有消息就根據(jù)消息做相應(yīng)的處理,Looper的另外一部分工作就是在循環(huán)代碼中會(huì)不斷從消息隊(duì)列挨個(gè)拿出消息給主線程處理。**MessageQueue **
MessageQueue 存在的原因很簡(jiǎn)單,就是同一線程在同一時(shí)間只能處理一個(gè)消息,同一線程代碼執(zhí)行是不具有并發(fā)性,所以需要隊(duì)列來(lái)保存消息和安排每個(gè)消息的處理順序。多個(gè)其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個(gè)列表(它同一時(shí)間不能處理那么多任務(wù)),然后挨個(gè)拿出來(lái)處理,這種設(shè)計(jì)很簡(jiǎn)單,我們平時(shí)寫代碼其實(shí)也經(jīng)常這么做。每一個(gè)Looper線程都會(huì)維護(hù)這樣一個(gè)隊(duì)列,而且僅此一個(gè),這個(gè)隊(duì)列的消息只能由該線程處理。Handler **
簡(jiǎn)單說(shuō)Handler用于同一個(gè)進(jìn)程的線程間通信。Looper讓主線程無(wú)限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那么怎樣在其他的線程往主線程的隊(duì)列里放入消息呢?其實(shí)很簡(jiǎn)單,我們知道在同一進(jìn)程中線程和線程之間資源是共享的,也就是對(duì)于任何變量在任何線程都是可以訪問(wèn)和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實(shí)例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時(shí)候就會(huì)在主線程**處理這個(gè)消息。那么怎么拿到主線程 MessageQueue的實(shí)例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類,那么這個(gè)Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時(shí)候就通過(guò)這個(gè)引用往消息隊(duì)列里插入新消息。Handler 的另外一個(gè)作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個(gè)Handler發(fā)出消息又確保消息處理也是自己來(lái)做,這樣的設(shè)計(jì)非常的贊。具體做法就是在隊(duì)列里面的Message持有Handler的引用(哪個(gè)handler 把它放到隊(duì)列里,message就持有了這個(gè)handler的引用),然后等到主線程輪詢到這個(gè)message的時(shí)候,就來(lái)回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法。**Message **
Message 很簡(jiǎn)單了,你想讓主線程做什么事,總要告訴它吧,總要傳遞點(diǎn)數(shù)據(jù)給它吧,Message就是這個(gè)載體。
源碼分析#
接下來(lái)我們會(huì)結(jié)合App主線程(UI線程)來(lái)講解,從App啟動(dòng)后一步一步往下走分析整個(gè)Android的消息處理機(jī)制,首先在ActivityThread類有我們熟悉的main的函數(shù),App啟動(dòng)的代碼的入口就在這里,UI線程本來(lái)只是一個(gè)普通線程,在這里會(huì)把UI線程轉(zhuǎn)換成Looper線程,什么是Looper線程,不急往下看就知道了。
public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
......
}
}
首先執(zhí)行的是 Looper.prepareMainLooper() 我們來(lái)看下Looper里面的這個(gè)方法做了什么?
注:看之前先稍微了解下ThreadLocal是什么?
ThreadLocal: 線程本地存儲(chǔ)區(qū)(Thread Local Storage,簡(jiǎn)稱為TLS),每個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域,不同線程之間彼此不能訪問(wèn)對(duì)方的TLS區(qū)域。這里線程自己的本地存儲(chǔ)區(qū)域存放是線程自己的Looper。具體看下ThreadLocal.java 的源碼!
public final class Looper {
// sThreadLocal 是static的變量,可以先簡(jiǎn)單理解它相當(dāng)于map,key是線程,value是Looper,
//那么你只要用當(dāng)前的線程就能通過(guò)sThreadLocal獲取當(dāng)前線程所屬的Looper。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//主線程(UI線程)的Looper 單獨(dú)處理,是static類型的,通過(guò)下面的方法getMainLooper()
//可以方便的獲取主線程的Looper。
private static Looper sMainLooper;
//Looper 所屬的線程的消息隊(duì)列
final MessageQueue mQueue;
//Looper 所屬的線程
final Thread mThread;
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//如果線程的TLS已有數(shù)據(jù),則會(huì)拋出異常,一個(gè)線程只能有一個(gè)Looper,prepare不能重復(fù)調(diào)用。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//往線程的TLS插入數(shù)據(jù),簡(jiǎn)單理解相當(dāng)于map.put(Thread.currentThread(),new Looper(quitAllowed));
sThreadLocal.set(new Looper(quitAllowed));
}
//實(shí)際上是調(diào)用 prepare(false),并然后給sMainLooper賦值。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//static 方法,方便獲取主線程的Looper.
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
public static @Nullable Looper myLooper() {
//具體看ThreadLocal類的源碼的get方法,
//簡(jiǎn)單理解相當(dāng)于map.get(Thread.currentThread()) 獲取當(dāng)前線程的Looper
return sThreadLocal.get();
}
}
看了上面的代碼(仔細(xì)看下注釋),我們發(fā)現(xiàn) Looper.prepareMainLooper()做的事件就是new了一個(gè)Looper實(shí)例并放入Looper類下面一個(gè)static的ThreadLocal<Looper> sThreadLocal靜態(tài)變量中,同時(shí)給sMainLooper賦值,給sMainLooper賦值是為了方便通過(guò)Looper.getMainLooper()快速獲取主線程的Looper,sMainLooper是主線程的Looper可能獲取會(huì)比較頻繁,避免每次都到 sThreadLocal 去查找獲取。
接下來(lái)重點(diǎn)是看下Looper的構(gòu)造函數(shù),看看在new Looper的時(shí)候做了什么事?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
看到?jīng)]有,這時(shí)候給當(dāng)前線程創(chuàng)建了消息隊(duì)列MessageQueue,并且讓Looper持有MessageQueue的引用。執(zhí)行完Looper.prepareMainLooper() 之后,主線程從普通線程轉(zhuǎn)成一個(gè)Looper線程。目前的主線程線程已經(jīng)有一個(gè)Looper對(duì)象和一個(gè)消息隊(duì)列mQueue,引用關(guān)系如下圖:(主線程可以輕松獲取它的Looper,主線程的Looper持有主線程消息隊(duì)列的引用)

具體如何獲取主線程的Looper對(duì)象和消息列表呢?
//在主線程中執(zhí)行
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue
//或者
mLooper=Looper.getMainLooper()
接下來(lái)回到ActivityThread 的main函數(shù),執(zhí)行完Looper.prepareMainLooper() 之后下一句代碼是ActivityThread thread = new ActivityThread();這句話就是創(chuàng)建一下ActivityThread對(duì)象,這邊需要注意的時(shí)候ActivityThread并不是一個(gè)線程,它并沒(méi)有繼承Thread,而只是一個(gè)普通的類public final class ActivityThread{...}ActivityThread的構(gòu)造函數(shù)并沒(méi)有做什么事只是初始化了資源管理器。
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
接著往下看下一行代碼
ActivityThread thread = new ActivityThread();
//建立Binder通道 (創(chuàng)建新線程)
thread.attach(false);
thread.attach(false);便會(huì)創(chuàng)建一個(gè)Binder線程(具體是指ApplicationThread,該Binder線程會(huì)通過(guò)想 Handler將Message發(fā)送給主線程,之后講)。我們之前提到主線程最后會(huì)進(jìn)入無(wú)限循環(huán)當(dāng)中,如果沒(méi)有在進(jìn)入死循環(huán)之前創(chuàng)建其他線程,那么待會(huì)誰(shuí)會(huì)給主線程發(fā)消息呢?,沒(méi)錯(cuò)就是在這里創(chuàng)建了這個(gè)線程,這個(gè)線程會(huì)接收來(lái)自系統(tǒng)服務(wù)發(fā)送來(lái)的一些事件封裝了Message并發(fā)送給主線程,主線程在無(wú)限循環(huán)中從隊(duì)列里拿到這些消息并處理這些消息。(Binder線程發(fā)生的消息包括LAUNCH_ACTIVITY,PAUSE_ACTIVITY 等等)
繼續(xù)回到mian 函數(shù)的下一句代碼Looper.loop() 那么重點(diǎn)來(lái)了,我們來(lái)看下Looper.loop()的源碼:
public static void loop() {
final Looper me = myLooper(); //獲取TLS存儲(chǔ)的Looper對(duì)象,獲取當(dāng)前線程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //獲取Looper對(duì)象中的消息隊(duì)列
....
for (;;) { //主線程開啟無(wú)限循環(huán)模式
Message msg = queue.next(); //獲取隊(duì)列中的下一條消息,可能會(huì)線程阻塞
if (msg == null) { //沒(méi)有消息,則退出循環(huán),退出消息循環(huán),那么你的程序也就可以退出了
return;
}
....
//分發(fā)Message,msg.target 是一個(gè)Handler對(duì)象,哪個(gè)Handler把這個(gè)Message發(fā)到隊(duì)列里,
//這個(gè)Message會(huì)持有這個(gè)Handler的引用,并放到自己的target變量中,這樣就可以回調(diào)我們重寫
//的handler的handleMessage方法。
msg.target.dispatchMessage(msg);
....
....
msg.recycleUnchecked(); //將Message回收到消息池,下次要用的時(shí)候不需要重新創(chuàng)建,obtain()就可以了。
}
}
上面的代碼,大家具體看下注釋,這時(shí)候主線程(UI線程)執(zhí)行到這一步就進(jìn)入了死循環(huán),不斷地去拿消息隊(duì)列里面的消息出來(lái)處理?那么問(wèn)題來(lái)了
1、UI線程一直在這個(gè)循環(huán)里跳不出來(lái),主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死嗎,那還怎么執(zhí)行其他的操作呢?
- 在looper啟動(dòng)后,主線程上執(zhí)行的任何代碼都是被looper從消息隊(duì)列里取出來(lái)執(zhí)行的。也就是說(shuō)主線程之后都是通過(guò)其他線程給它發(fā)消息來(lái)實(shí)現(xiàn)執(zhí)行其他操作的。生命周期的回調(diào)也是如此的,系統(tǒng)服務(wù)ActivityManagerService通過(guò)Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后,通過(guò)App進(jìn)程的Binder線程給主線程的消息隊(duì)列插入一條消息來(lái)實(shí)現(xiàn)的。
2、主線程是UI線程和用戶交互的線程,優(yōu)先級(jí)應(yīng)該很高,主線程的死循環(huán)一直運(yùn)行是不是會(huì)特別消耗CPU資源嗎?App進(jìn)程的其他線程怎么辦?
- 這基本是一個(gè)類似生產(chǎn)者消費(fèi)者的模型,簡(jiǎn)單說(shuō)如果在主線程的MessageQueue沒(méi)有消息時(shí),就會(huì)阻塞在loop的queue.next()方法里,這時(shí)候主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個(gè)消息進(jìn)來(lái)時(shí)候就會(huì)喚醒主線程,在2.2 版本以前,這套機(jī)制是用我們熟悉的線程的wait和notify 來(lái)實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制,通過(guò)往pipe管道寫端寫入數(shù)據(jù)來(lái)喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。
所以上面代碼的重點(diǎn)是queue.next() 的函數(shù),其他的我們就不多說(shuō)了,我們來(lái)看下queue.next()的源碼(主要還是看注釋):
Message next()
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//nextPollTimeoutMillis 表示nativePollOnce方法需要等待nextPollTimeoutMillis
//才會(huì)返回
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//讀取消息,隊(duì)里里沒(méi)有消息有可能會(huì)堵塞,兩種情況該方法才會(huì)返回(代碼才能往下執(zhí)行)
//一種是等到有消息產(chǎn)生就會(huì)返回,
//另一種是當(dāng)?shù)攘薾extPollTimeoutMillis時(shí)長(zhǎng)后,nativePollOnce也會(huì)返回
nativePollOnce(ptr, nextPollTimeoutMillis);
//nativePollOnce 返回之后才能往下執(zhí)行
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 循環(huán)找到一條不是異步而且msg.target不為空的message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 雖然有消息,但是還沒(méi)有到運(yùn)行的時(shí)候,像我們經(jīng)常用的postDelay,
//計(jì)算出離執(zhí)行時(shí)間還有多久賦值給nextPollTimeoutMillis,
//表示nativePollOnce方法要等待nextPollTimeoutMillis時(shí)長(zhǎng)后返回
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取到消息
mBlocked = false;
//鏈表一些操作,獲取msg并且刪除該節(jié)點(diǎn)
if (prevMsg != null)
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
//返回拿到的消息
return msg;
}
} else {
//沒(méi)有消息,nextPollTimeoutMillis復(fù)位
nextPollTimeoutMillis = -1;
}
.....
.....
}
nativePollOnce()很重要,是一個(gè)native的函數(shù),在native做了大量的工作,主要涉及到epoll機(jī)制的處理(在沒(méi)有消息處理時(shí)阻塞在管道的讀端),具體關(guān)于native相關(guān)的源碼本篇文章不涉及,感興趣的同學(xué)可以網(wǎng)上找找,有不少分析得比較深。
分析到這里,從應(yīng)用啟動(dòng)創(chuàng)建Looper,創(chuàng)建消息隊(duì)列,到進(jìn)入loop方法執(zhí)行無(wú)限循環(huán)中,那么這一塊就告一段落了,主線程已經(jīng)在死循環(huán)里輪詢等待消息了,接下來(lái)我們就要再看看,系統(tǒng)是怎么發(fā)消息給主線程的,主線程是怎么處理這些個(gè)消息的?
在準(zhǔn)備啟動(dòng)一個(gè)Activity的時(shí)候,系統(tǒng)服務(wù)進(jìn)程下的ActivityManagerService(簡(jiǎn)稱AMS)線程會(huì)通過(guò)Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后,通過(guò)App進(jìn)程下的Binder線程最終調(diào)用ActivityThread類下面的scheduleLaunchActivity方法來(lái)準(zhǔn)備啟動(dòng)Activity,看下scheduleLaunchActivity方法:
注:Binder線程:具體是指ApplicationThread,在App進(jìn)程中接受系統(tǒng)進(jìn)程傳遞過(guò)來(lái)的信息的線程(在主線程進(jìn)入死循環(huán)之前創(chuàng)建了這個(gè)線程)。
//這個(gè)方法不是在主線程調(diào)用,是Binder線程下調(diào)用的
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
把啟動(dòng)一些信息封裝成ActivityClientRecord之后,最后一句調(diào)用sendMessage(H.LAUNCH_ACTIVITY, r);我們接著往下看:
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
看到?jīng)]有,最后啟動(dòng)Activity的信息都封裝一個(gè)Message,但是這里有個(gè)問(wèn)題了,之前在分析main函數(shù)的時(shí)候,完全沒(méi)給出往主線程消息隊(duì)列插入消息的方式,這里有了消息,但是怎么發(fā)到主線程的消息隊(duì)列呢?最后一句又是重點(diǎn)mH.sendMessage(msg); mH 是什么呢?難道是Handler,我們來(lái)看下它是什么東西?
我們看了下ActivityThread 的成員變量,發(fā)現(xiàn)一句初始化的代碼
final H mH = new H();
繼續(xù)往下看H是什么?
public final class ActivityThread{
....
final H mH = new H();
....
private class H extends Handler {
....
....
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
.....
}
.....
.....
}
}
H 果不出其然是Handler,而且是ActivityThread的內(nèi)部類,看了一下它的handleMessage 方法,LAUNCH_ACTIVITY、PAUSE_ACTIVITY、RESUME_ACTIVITY...好多好多,H 類幫我們處理了好多聲明周期的事情。那么再回到mH.sendMessage(msg)這句代碼上,在Binder線程執(zhí)行mH.sendMessage(msg);,由主線程創(chuàng)建的Handler mH實(shí)例發(fā)送消息到主線程的消息隊(duì)列里,消息隊(duì)列從無(wú)到有,主線程堵塞被喚醒,主線程loop拿到消息,并回調(diào)mH的handleMessage 方法處理LAUNCH_ACTIVITY 等消息。從而實(shí)現(xiàn)Activity的啟動(dòng)。
講到這里,基本一個(gè)啟動(dòng)流程分析完了,大家可能比較想知道的是 mH.sendMessage(msg); 關(guān)于Hanlder是怎么把消息發(fā)到主線程的消息隊(duì)列的?我們接下來(lái)就講解下Handler,首先看下Handler的源碼!我們先來(lái)看看我們經(jīng)常用的Handler的無(wú)參構(gòu)造函數(shù),實(shí)際調(diào)用的是Handler(Callback callback, boolean async)構(gòu)造函數(shù)(看注釋)
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//不是static 發(fā)出可能內(nèi)存泄露的警告!
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//獲取當(dāng)前線程的Looper,還記得前面講過(guò) Looper.myLooper()方法了嗎?
//Looper.myLooper()內(nèi)部實(shí)現(xiàn)可以先簡(jiǎn)單理解成:map.get(Thread.currentThread())
//獲取當(dāng)前線程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
//當(dāng)前線程不是Looper 線程,沒(méi)有調(diào)用Looper.prepare()給線程創(chuàng)建Looper對(duì)象
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//讓Handler 持有當(dāng)前線程消息隊(duì)列的引用
mQueue = mLooper.mQueue;
//這些callback先不管,主要用于handler的消息發(fā)送的回調(diào),優(yōu)先級(jí)是比handlerMessage高,但是不常用
mCallback = callback;
mAsynchronous = async;
}
上面的代碼說(shuō)明了下面幾個(gè)問(wèn)題:
1、Handler 對(duì)象在哪個(gè)線程下構(gòu)建(Handler的構(gòu)造函數(shù)在哪個(gè)線程下調(diào)用),那么Handler 就會(huì)持有這個(gè)線程的Looper引用和這個(gè)線程的消息隊(duì)列的引用。因?yàn)槌钟羞@個(gè)線程的消息隊(duì)列的引用,意味著這個(gè)Handler對(duì)象可以在任意其他線程給該線程的消息隊(duì)列添加消息,也意味著Handler的handlerMessage 肯定也是在該線程執(zhí)行的。
2、如果該線程不是Looper線程,在這個(gè)線程new Handler 就會(huì)報(bào)錯(cuò)!
3、上面兩點(diǎn)綜合說(shuō)明了下面一段很常見的代碼:把普通線程轉(zhuǎn)成Looper線程的代碼,為什么在Looper.prepare()和Looper.loop()中間要?jiǎng)?chuàng)建Handler:
class LooperThread extends Thread {
//其他線程可以通過(guò)mHandler這個(gè)引用給該線程的消息隊(duì)列添加消息
public Handler mHandler;
public void run() {
Looper.prepare();
//需要在線程進(jìn)入死循環(huán)之前,創(chuàng)建一個(gè)Handler實(shí)例供外界線程給自己發(fā)消息
mHandler = new Handler() {
public void handleMessage(Message msg) {
//Handler 對(duì)象在這個(gè)線程構(gòu)建,那么handleMessage的方法就在這個(gè)線程執(zhí)行
}
};
Looper.loop();
}
}
那么接下來(lái),我們接著往下看Handler的sendMessage(msg)方法,這個(gè)方法也是比較重要的,也比較常用,Handler 有很多sendXXXX開頭的方法sendMessageAtTime、sendEmptyMessageDelayed、sendEmptyMessage等等,都是用來(lái)給消息隊(duì)列添加消息的,那么這些方法最終都會(huì)調(diào)用enqueueMessage來(lái)實(shí)現(xiàn)消息進(jìn)入隊(duì)列:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//這句話很重要,讓消息持有當(dāng)前Handler的引用,在消息被Looper線程輪詢到的時(shí)候
//回調(diào)handler的handleMessage方法
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//調(diào)用MessageQueue 的enqueueMessage 方法把消息放入隊(duì)列
return queue.MessageQueue(msg, uptimeMillis);
}
我們?cè)賮?lái)看下MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:
boolean enqueueMessage(Message msg, long when) {
// msg 必須有target也就是必須有handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//插入消息隊(duì)列的時(shí)候需要做同步,因?yàn)闀?huì)有多個(gè)線程同時(shí)做往這個(gè)隊(duì)列插入消息
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
//when 表示這個(gè)消息執(zhí)行的時(shí)間,隊(duì)列是按照消息執(zhí)行時(shí)間排序的
//如果handler 調(diào)用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillis
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// p==null 表示當(dāng)前消息隊(duì)列沒(méi)有消息
msg.next = p;
mMessages = msg;
//需要喚醒主線程,如果隊(duì)列沒(méi)有元素,主線程會(huì)堵塞在管道的讀端,這時(shí)
//候隊(duì)列突然有消息了,就會(huì)往管道寫入字符,喚醒主線程
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//將消息放到隊(duì)列的確切位置,隊(duì)列是按照msg的when 排序的,鏈表操作自己看咯
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 如果需要喚醒Looper線程,這里調(diào)用native的方法實(shí)現(xiàn)epoll機(jī)制喚醒線程,我們就不在深入探討了
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
最后我們?cè)倏聪翲andler 的dispatchMessage方法,這個(gè)方法在Looper線程從消息隊(duì)列拿出來(lái)的時(shí)候,通過(guò)msg.target.dispatchMessage(msg)調(diào)用的。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//優(yōu)先調(diào)用callback方法
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//最后會(huì)回調(diào)我們重寫的handleMessage 方法
handleMessage(msg);
}
}
到這里,整個(gè)Android的消息處理機(jī)制Java層內(nèi)容基本講解完畢了(歡迎關(guān)注我的簡(jiǎn)書:Kelin)
注:【轉(zhuǎn)載請(qǐng)注明,問(wèn)題可留言,錯(cuò)誤望指出,喜歡可打賞,博客持續(xù)更新,歡迎關(guān)注】