概述
Handler通常被我們開發(fā)者用來從子線程中發(fā)送消息通知主線程跟新UI豫领,但是這只是Handle的冰山一角伏嗜,本文從源碼的角度徹底解析Handler消息機(jī)制的原理。
Handler機(jī)制只要涉及的幾個類:
主要從以下方面來分析整個源碼:
- Handler窗声、Looper蘑拯、MessageQueue巧颈、Message的源碼
- mainThread 的 handler 和 looper(從ActivityThread.main開始)
- 整個消息發(fā)送的流程
- 分析每個環(huán)節(jié)的代碼細(xì)節(jié)
- Handler、Looper和Thread之間的關(guān)系
- 由handler造成的內(nèi)存泄露問題
整個消息發(fā)送袖扛、接收流程的大致梳理
第一步:handle.sendMessage()把消息發(fā)送給對應(yīng)線程的Looper的消息列隊(每個looper持有一個MessageQueue)
第二步:Looer.loop()不斷輪詢?nèi)〕鱿?br>
第三步:取出消息后發(fā)送給對應(yīng)的Handler
系統(tǒng)主線程中的Handler
Handler在Android的整個架構(gòu)中間不僅僅只是負(fù)責(zé)更新UI的作用砸泛,通過分析AcitivityThread的源碼可以看到,系統(tǒng)一開始幫我們初始化一個主線程的Handler和Looper,Activity蛆封、Service等的生命周期的消息都是經(jīng)過Handler來處理的唇礁,可見Handler的重要性。我們都知道Java的程序入口是main函數(shù)惨篱,Android自然也是一樣
AcitivityThread#main
final H mH = new H();
public static void main(String[] args) {
...
//在main線程里初始化一個Looper
Looper.prepareMainLooper();
//開啟死循環(huán)盏筐,不斷輪詢MessageQueue一旦有消息,從列隊中取出發(fā)送
Looper.loop();
...
}
private class H extends Handler {
}
首先調(diào)用Looper.prepareMainLooper()砸讳,初始化一個Looper將Looper和main線程綁定,然后在AcitivityThread里面懶加載了一個H變量琢融,H繼承的Handler,最后Looper.loop()開啟死循環(huán)簿寂,不斷輪詢MessageQueue一旦有消息漾抬,從列隊中取出發(fā)送。
接下來看看H.handleMessage()干了哪些事情常遂,抽取幾個分析:
public void handleMessage(Message msg) {
...
case BIND_SERVICE://綁定服務(wù)
...
case PAUSE_ACTIVITY://Activity的生命周期控制
...
case EXIT_APPLICATION://退出應(yīng)用
Looper.myLooper().quit();
break;
}
可以看到Android系統(tǒng)的消息機(jī)制纳令、事件反饋機(jī)制等的消息都是由Handler來進(jìn)行分發(fā)的。
看到這里可能大家會有這么一個疑問克胳,那就是看到AcitivityThread.main方法里面就寥寥幾行代碼平绩,main方法運(yùn)行完了整個App程序不也就退出了嗎?為什么App能夠一直存在呢漠另?因為Looper.loop()里面執(zhí)行了一個死循環(huán)捏雌。循環(huán)退出了,main函數(shù)執(zhí)行完了酗钞,程序也就退出了腹忽,這就是上面Looper.myLooper().quit()退出looper的死循環(huán)就退出應(yīng)用的原因来累。
到這里,我們看到要通過Handler向一個線程發(fā)送消息要有這幾個步驟:
- 初始化一個Looper和線程綁定起來窘奏,一個線程最多只能有一個Looper(具體原因后面分析)
- 初始化一個Handler嘹锁,用來發(fā)送消息給Looper的MessageQueue
- Looper.loop()開啟死循環(huán),不斷輪詢MessageQueue取出消息發(fā)送
我們平時常在主線程中new的Handler不用經(jīng)歷這些步驟着裹,那是應(yīng)為在app啟動的時候系統(tǒng)幫我們已經(jīng)做好了领猾。
到這里,有幾個疑問就是:
Handler是怎么和Looper綁定的呢?明明new Handle的時候和Looper半毛錢關(guān)系都沒有
多個Handler怎么可以同時向一個Looper的MessageQueue發(fā)送消息呢骇扇?(ActicityThread中的H和我們平時用的handler就是)
Looper
前面提到的摔竿,Handler的使用第一步就是Looper.prepare(),初始化Looper與線程綁定少孝。
public class Looper {
//全局的sThreadLocal
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper內(nèi)的消息隊列
final MessageQueue mQueue;
// 當(dāng)前線程
Thread mThread;
// 继低。。稍走。其他屬性
// 每個Looper對象中有它的消息隊列袁翁,和它所屬的線程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我們調(diào)用該方法會在調(diào)用線程的TLS中創(chuàng)建Looper對象
public static final void prepare() {
//從當(dāng)前線程拿Looper
if (sThreadLocal.get() != null) {
//如果線程中已經(jīng)初始化了Looper,則拋出異常婿脸,這就是為什么一個線程只能有Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
//new 一個Looper存放到線程
sThreadLocal.set(new Looper());
}
// 其他方法
}
prepare()首先調(diào)用sThreadLocal.get()粱胜,會去檢測當(dāng)前線程中是否已經(jīng)初始化了Looper,如果已經(jīng)有Looper狐树,則拋異常焙压,這里就規(guī)定了一個線程只能有一個Looper。如果多次調(diào)用Looper.prepare的話就會拋異常抑钟。然后調(diào)用sThreadLocal.set(new Looper()),將Looper保存到對應(yīng)的Thread中涯曲。發(fā)現(xiàn)沒有Looper的構(gòu)造是private的,所有的Looper實(shí)列化都必須通過prepare()在塔,Looper構(gòu)造里面初始化了MessageQueue對象掀抹,所以每個Looper擁有一個消息隊列。
Looper和Thread能夠綁定的關(guān)鍵在于sThreadLocal.set()心俗,ThreadLocal是用于線程安全的傲武,具體的不做討論。sThreadLocal是一個靜態(tài)變量城榛,系統(tǒng)初始化的時候就存在的揪利。進(jìn)一步看sThreadLocal.set()的源碼
public void set(T value) {
//獲取當(dāng)前線程
Thread currentThread = Thread.currentThread();
//拿到Thread中的localValues變量
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
//將全局的ThreadLocal(Looper.sThreadLocal)作為鍵,Looper作為值存到線程的ThreadLocal.Values localValues變量中
values.put(this, value);
}
Values values(Thread current) {
return current.localValues;
}
class Thread{
ThreadLocal.Values localValues;
}
首先拿到當(dāng)前線程狠持,獲取Thread的一個變量localValues疟位,Values 是ThreadLocal的一個內(nèi)部類,以鍵值對存放數(shù)據(jù)喘垂,是一個散列鏈表甜刻,和HashMap類似绍撞。Looper就是這樣和Thread綁定的。
looper()方法:
public static final void loop() {
Looper me = myLooper(); //得到當(dāng)前線程Looper
MessageQueue queue = me.mQueue; //得到當(dāng)前l(fā)ooper的MQ
// 開始循環(huán)
for (;;) {
Message msg = queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
// message沒有target為結(jié)束信號得院,退出循環(huán)
return;
}
// 非常重要傻铣!將真正的處理工作交給message的target,即后面要講的handler
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
}
myLooper()方法主要是通過sThreadLocal去拿到當(dāng)前線程的Looper對象
然后拿到消息隊列mQueue
public static Looper myLooper() {
return sThreadLocal.get();
}
再通過queue.next()從消息隊列中取出消息msg祥绞,然后msg.target.dispatchMessage(msg);把消息發(fā)送給handler非洲,msg.target就是handler的引用,是在Handler.sendMessage的時候賦值的蜕径,調(diào)用handler.dispatchMessage()方法最后會調(diào)用我們熟悉的handler.handlerMessage()方法两踏,來讓我們處理消息。
最后一步msg.recycleUnchecked()把消息回收放入消息池內(nèi)兜喻。
小結(jié):
Looper.prepare()方法初始化一個Looper對象同時為Looper實(shí)例化了一個MessageQueue梦染,并且通過全局變量sThreadLocal將初始化Looper的線程和Looper對象關(guān)聯(lián),將Looper對象存儲在當(dāng)前Thread變量中朴皆。
Looper.loop()方法正式將Looper運(yùn)行起來弓坞,不斷循環(huán)從消息隊列取消、發(fā)送消息车荔、回收消息。
Handler
Handler使用的時候分兩步:
new Handler 戚扳、handler.sendMessage()或者h(yuǎn)andler.post()忧便,這時候Looper影子都沒見著,Handler是怎么把消息發(fā)送到線程的Looper.mQueue中去的呢帽借?答案就在Handler的構(gòu)造中:
public Handler(Callback callback, boolean async) {
...
//拿到當(dāng)前線程的Looper
mLooper = Looper.myLooper();
//這里表示在初始化Handler之前珠增,必須初始化Looper,不然拋異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//拿到消息列隊
mQueue = mLooper.mQueue;
//自定義回調(diào)
mCallback = callback;
mAsynchronous = async;
}
首先在構(gòu)造里面會去拿到與當(dāng)前線程(也就是初始化Handler的線程)的Looper砍艾、MessageQueue蒂教,賦值給Handler的成員變量,這里就解釋了Handler怎么和Looper怎么關(guān)聯(lián)起來的了而且多個handler可以同時向同一個Looper的消息列隊發(fā)送消息脆荷,因為他們拿到的是同一個Looper凝垛,中間的紐帶就是Thread。
在來看sendMessage()蜓谋,通過幾個重載的方法最終調(diào)用下面函數(shù):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//直接把關(guān)聯(lián)looper的MessageQueue 作為自己的MessageQueue 梦皮,因此它的消息將發(fā)送到關(guān)聯(lián)looper的MessageQueue 上
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到msg.target = this將當(dāng)前的Handler賦值給了Message的變量,這就是為什么Looper在取出Message的時候能夠準(zhǔn)確的發(fā)送給相應(yīng)的Handler桃焕。最終把消息放入消息隊列剑肯。uptimeMillis是延時發(fā)送的時間,最終在queue.enqueueMessage(msg, uptimeMillis)會存入msg中观堂。
在Handler中的另外幾個點(diǎn):
1让网、handler.post(runnable)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
最終同樣也是調(diào)用sendMessageAtTime(),以前老以為這里Runable是和線程有關(guān)呀忧,看源碼才知道和線程半毛錢關(guān)系都沒有,僅僅只是一個接口溃睹,在看getPostMessage(r):
private static Message getPostMessage(Runnable r, Object token) {
//得到一個消息實(shí)例
Message m = Message.obtain();
m.obj = token;
//把Runable對象賦值給了Message的一個變量最終返回msg
m.callback = r;
return m;
}
Runnable 對象賦值給了Message的一個變量而账,最終還是把返回的Msg,之后流程和handler.sendMessage()一樣的了.
在Looper.loop()中msg.target.dispatchMessage(msg)來發(fā)送消息最終調(diào)用的是Handler.dispatchMessage()方法丸凭,
public void dispatchMessage(Message msg) {
//如果是handler.post(Runable)走這里
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//自定義回調(diào)
if (mCallback.handleMessage(msg)) {
return;
}
}
//常規(guī)的回調(diào)
handleMessage(msg);
}
}
首先會去檢查msg是否有callback 也就是上面講的Runable對象福扬,如果有則是我們常用的handler.post()這種調(diào)用方式,接下來回去判斷mCallback 是否為空惜犀,這個mCallback 是Handler的內(nèi)部類铛碑,在Handler的重載的構(gòu)造中賦值,如果是自定義的回調(diào)則調(diào)用mCallback.handleMessage()虽界,這樣就不會回調(diào)handler.handlerMessage(),最后調(diào)用handler.handlerMessage();
到此消息發(fā)送汽烦、接收的原理和流程就分析完了。這里還有個經(jīng)典的案列:向子線程中發(fā)送消息莉御。
其實(shí)Looper的源碼注釋已經(jīng)寫得很明白了撇吞,也給出了示例代碼:
public Handler mHandler;
class LooperThread extends Thread {
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
}
mHandler.sendMessage();
首先:初始化子線程的Looper礁叔,關(guān)聯(lián)Looper和子線程
其次:初始化Hander牍颈,拿到當(dāng)前線程的Looper賦值給Handler最為成員變量
最后:Looper.loope()死循環(huán),不斷從消息列隊取消息
最終handler.handlerMessage()在Looper.loop()中被調(diào)用琅关,所以handlerMessage()運(yùn)行在子線程中煮岁。
未完