概述
Handler通常被我們開發者用來從子線程中發送消息通知主線程跟新UI,但是這只是Handle的冰山一角,本文從源碼的角度徹底解析Handler消息機制的原理。
Handler機制只要涉及的幾個類:
主要從以下方面來分析整個源碼:
- Handler、Looper、MessageQueue、Message的源碼
- mainThread 的 handler 和 looper(從ActivityThread.main開始)
- 整個消息發送的流程
- 分析每個環節的代碼細節
- Handler、Looper和Thread之間的關系
- 由handler造成的內存泄露問題
整個消息發送、接收流程的大致梳理
第一步:handle.sendMessage()把消息發送給對應線程的Looper的消息列隊(每個looper持有一個MessageQueue)
第二步:Looer.loop()不斷輪詢取出消息
第三步:取出消息后發送給對應的Handler
系統主線程中的Handler
Handler在Android的整個架構中間不僅僅只是負責更新UI的作用,通過分析AcitivityThread的源碼可以看到,系統一開始幫我們初始化一個主線程的Handler和Looper,Activity、Service等的生命周期的消息都是經過Handler來處理的,可見Handler的重要性。我們都知道Java的程序入口是main函數,Android自然也是一樣
AcitivityThread#main
final H mH = new H();
public static void main(String[] args) {
...
//在main線程里初始化一個Looper
Looper.prepareMainLooper();
//開啟死循環,不斷輪詢MessageQueue一旦有消息,從列隊中取出發送
Looper.loop();
...
}
private class H extends Handler {
}
首先調用Looper.prepareMainLooper(),初始化一個Looper將Looper和main線程綁定,然后在AcitivityThread里面懶加載了一個H變量,H繼承的Handler,最后Looper.loop()開啟死循環,不斷輪詢MessageQueue一旦有消息,從列隊中取出發送。
接下來看看H.handleMessage()干了哪些事情,抽取幾個分析:
public void handleMessage(Message msg) {
...
case BIND_SERVICE://綁定服務
...
case PAUSE_ACTIVITY://Activity的生命周期控制
...
case EXIT_APPLICATION://退出應用
Looper.myLooper().quit();
break;
}
可以看到Android系統的消息機制、事件反饋機制等的消息都是由Handler來進行分發的。
看到這里可能大家會有這么一個疑問,那就是看到AcitivityThread.main方法里面就寥寥幾行代碼,main方法運行完了整個App程序不也就退出了嗎?為什么App能夠一直存在呢?因為Looper.loop()里面執行了一個死循環。循環退出了,main函數執行完了,程序也就退出了,這就是上面Looper.myLooper().quit()退出looper的死循環就退出應用的原因。
到這里,我們看到要通過Handler向一個線程發送消息要有這幾個步驟:
- 初始化一個Looper和線程綁定起來,一個線程最多只能有一個Looper(具體原因后面分析)
- 初始化一個Handler,用來發送消息給Looper的MessageQueue
- Looper.loop()開啟死循環,不斷輪詢MessageQueue取出消息發送
我們平時常在主線程中new的Handler不用經歷這些步驟,那是應為在app啟動的時候系統幫我們已經做好了。
到這里,有幾個疑問就是:
Handler是怎么和Looper綁定的呢?明明new Handle的時候和Looper半毛錢關系都沒有
多個Handler怎么可以同時向一個Looper的MessageQueue發送消息呢?(ActicityThread中的H和我們平時用的handler就是)
Looper
前面提到的,Handler的使用第一步就是Looper.prepare(),初始化Looper與線程綁定。
public class Looper {
//全局的sThreadLocal
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper內的消息隊列
final MessageQueue mQueue;
// 當前線程
Thread mThread;
// 。。。其他屬性
// 每個Looper對象中有它的消息隊列,和它所屬的線程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我們調用該方法會在調用線程的TLS中創建Looper對象
public static final void prepare() {
//從當前線程拿Looper
if (sThreadLocal.get() != null) {
//如果線程中已經初始化了Looper,則拋出異常,這就是為什么一個線程只能有Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
//new 一個Looper存放到線程
sThreadLocal.set(new Looper());
}
// 其他方法
}
prepare()首先調用sThreadLocal.get(),會去檢測當前線程中是否已經初始化了Looper,如果已經有Looper,則拋異常,這里就規定了一個線程只能有一個Looper。如果多次調用Looper.prepare的話就會拋異常。然后調用sThreadLocal.set(new Looper()),將Looper保存到對應的Thread中。發現沒有Looper的構造是private的,所有的Looper實列化都必須通過prepare(),Looper構造里面初始化了MessageQueue對象,所以每個Looper擁有一個消息隊列。
Looper和Thread能夠綁定的關鍵在于sThreadLocal.set(),ThreadLocal是用于線程安全的,具體的不做討論。sThreadLocal是一個靜態變量,系統初始化的時候就存在的。進一步看sThreadLocal.set()的源碼
public void set(T value) {
//獲取當前線程
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;
}
首先拿到當前線程,獲取Thread的一個變量localValues,Values 是ThreadLocal的一個內部類,以鍵值對存放數據,是一個散列鏈表,和HashMap類似。Looper就是這樣和Thread綁定的。
looper()方法:
public static final void loop() {
Looper me = myLooper(); //得到當前線程Looper
MessageQueue queue = me.mQueue; //得到當前looper的MQ
// 開始循環
for (;;) {
Message msg = queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
// message沒有target為結束信號,退出循環
return;
}
// 非常重要!將真正的處理工作交給message的target,即后面要講的handler
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
}
myLooper()方法主要是通過sThreadLocal去拿到當前線程的Looper對象
然后拿到消息隊列mQueue
public static Looper myLooper() {
return sThreadLocal.get();
}
再通過queue.next()從消息隊列中取出消息msg,然后msg.target.dispatchMessage(msg);把消息發送給handler,msg.target就是handler的引用,是在Handler.sendMessage的時候賦值的,調用handler.dispatchMessage()方法最后會調用我們熟悉的handler.handlerMessage()方法,來讓我們處理消息。
最后一步msg.recycleUnchecked()把消息回收放入消息池內。
小結:
Looper.prepare()方法初始化一個Looper對象同時為Looper實例化了一個MessageQueue,并且通過全局變量sThreadLocal將初始化Looper的線程和Looper對象關聯,將Looper對象存儲在當前Thread變量中。
Looper.loop()方法正式將Looper運行起來,不斷循環從消息隊列取消、發送消息、回收消息。
Handler
Handler使用的時候分兩步:
new Handler 、handler.sendMessage()或者handler.post(),這時候Looper影子都沒見著,Handler是怎么把消息發送到線程的Looper.mQueue中去的呢?答案就在Handler的構造中:
public Handler(Callback callback, boolean async) {
...
//拿到當前線程的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;
//自定義回調
mCallback = callback;
mAsynchronous = async;
}
首先在構造里面會去拿到與當前線程(也就是初始化Handler的線程)的Looper、MessageQueue,賦值給Handler的成員變量,這里就解釋了Handler怎么和Looper怎么關聯起來的了而且多個handler可以同時向同一個Looper的消息列隊發送消息,因為他們拿到的是同一個Looper,中間的紐帶就是Thread。
在來看sendMessage(),通過幾個重載的方法最終調用下面函數:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//直接把關聯looper的MessageQueue 作為自己的MessageQueue ,因此它的消息將發送到關聯looper的MessageQueue 上
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到msg.target = this將當前的Handler賦值給了Message的變量,這就是為什么Looper在取出Message的時候能夠準確的發送給相應的Handler。最終把消息放入消息隊列。uptimeMillis是延時發送的時間,最終在queue.enqueueMessage(msg, uptimeMillis)會存入msg中。
在Handler中的另外幾個點:
1、handler.post(runnable)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
最終同樣也是調用sendMessageAtTime(),以前老以為這里Runable是和線程有關,看源碼才知道和線程半毛錢關系都沒有,僅僅只是一個接口,在看getPostMessage(r):
private static Message getPostMessage(Runnable r, Object token) {
//得到一個消息實例
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)來發送消息最終調用的是Handler.dispatchMessage()方法,
public void dispatchMessage(Message msg) {
//如果是handler.post(Runable)走這里
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//自定義回調
if (mCallback.handleMessage(msg)) {
return;
}
}
//常規的回調
handleMessage(msg);
}
}
首先會去檢查msg是否有callback 也就是上面講的Runable對象,如果有則是我們常用的handler.post()這種調用方式,接下來回去判斷mCallback 是否為空,這個mCallback 是Handler的內部類,在Handler的重載的構造中賦值,如果是自定義的回調則調用mCallback.handleMessage(),這樣就不會回調handler.handlerMessage(),最后調用handler.handlerMessage();
到此消息發送、接收的原理和流程就分析完了。這里還有個經典的案列:向子線程中發送消息。
其實Looper的源碼注釋已經寫得很明白了,也給出了示例代碼:
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,關聯Looper和子線程
其次:初始化Hander,拿到當前線程的Looper賦值給Handler最為成員變量
最后:Looper.loope()死循環,不斷從消息列隊取消息
最終handler.handlerMessage()在Looper.loop()中被調用,所以handlerMessage()運行在子線程中。
未完