Handler 對于 Android 開發(fā)來說簡直就是家常便飯忿檩,它的原理自然都很熟悉,這篇文章不會宏觀地去介紹它的原理爆阶,而是細節(jié)深入到各個組成燥透。
目錄
關(guān)系
開始深入細節(jié)的時候,我們可以先復習下 Handler 辨图、Looper 和 MessageQueue 三者的關(guān)系班套。
- Handler 必須在
Looper.prepare()
之后才能創(chuàng)建使用 - Looper 與當前線程關(guān)聯(lián),并且管理著一個 MessageQueue
- Message 是實現(xiàn) Parcelable 接口的類
- 以一個線程為基準故河,他們的數(shù)量級關(guān)系是:
Handler(N) : Looper(1) : MessageQueue(1) : Thread(1)
他們的調(diào)用關(guān)系可以參考這張圖:
分析
0x01
public Handler(Callback callback, boolean async) {
// 代碼省略
mLooper = Looper.myLooper();
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 默認的構(gòu)造函數(shù)我們可以看到吱韭,Handler 內(nèi)部會通過 Looper.myLooper()
來獲取 Looper 對象,從而與之關(guān)聯(lián)鱼的。
0x02
我們之前已經(jīng)知道 Looper 管理著消息隊列理盆,從這里深入進去看看是如何跟 MessageQueue 建立聯(lián)系瞻讽。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在 Looper.myLooper()
里我們看到,Looper 是通過 sThreadLocal.get()
來獲取熏挎,那么我們又是何時將 Looper 設(shè)置給 sThreadLocal 的呢速勇?答案就在 prepare()
方法里。
我們看到 sThreadLocal.set(new Looper(quitAllowed));
實例化了一個 Looper 對象給 sThreadLocal 并且一個線程只有一個 Looper 坎拐。
同時我也貼出了 prepareMainLooper()
方法烦磁,根據(jù)名字大家都可以猜到,這個方法就是在 Android 主線程(UI)線程調(diào)用的方法哼勇,而在這個方法里也調(diào)用了 prepare(false)
我們看到這里傳入的是 false 都伪,表明主線程這里的 Looper 是無法執(zhí)行 quit()
方法。
我在這里貼出 ActivityThread 的 Main()
方法的部分代碼积担,這也是我們程序的入口:
public static void main(String[] args) {
// 代碼省略
Looper.prepareMainLooper(); // 創(chuàng)建消息循環(huán) Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler(); // UI 線程的 Handler
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop(); // 執(zhí)行消息循環(huán)
}
在這里我們更清楚了為什么可以直接在主線程創(chuàng)建 Handler 陨晶,而不會發(fā)生異常。
以上帝璧,我們明白了 Looper 是通過 prepare()
方法與線程建立聯(lián)系先誉,同時不同線程是無法訪問對方的消息隊列。
為什么 Handler 要在主線程創(chuàng)建才能更新 UI 呢的烁?
因為 Handler 要與主線程的消息隊列關(guān)聯(lián)上褐耳,這樣 handleMessage()
才會執(zhí)行在 UI 線程。
0x03
Looper 的核心其實是它循環(huán)取出消息的代碼:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 死循環(huán)
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
/// Handler msg.target
msg.target.dispatchMessage(msg); // 派發(fā)消息
// 代碼省略
msg.recycleUnchecked();
}
}
從上面代碼我們可以看到渴庆,Looper 在 loop()
方法里建立了一個死循環(huán)铃芦,通過消息隊列里不斷的取出消息,交給 Handler 去處理襟雷。
這個時候你可能會有一個問題:
Android 中為什么主線程不會因為 Looper.loop() 里的死循環(huán)卡死刃滓?
我比較推薦 Gityuan 的回答
回到我們這里,在循環(huán)中是通過 msg.target.dispatchMessage(msg);
派發(fā)消息耸弄。其中 msg 是 Message 類型咧虎,簡單看看它的成員:
public final class Message implements Parcelable {
Handler target;
Runnable callback;
Message next;
public Object obj;
public int arg1;
public int arg2;
// 代碼省略
}
可以知道消息隊列是鏈表實現(xiàn)的,并且 target 是 Handler 類型叙赚。
現(xiàn)在就可以連通了老客,通過 Handler 將 Message 投遞給消息隊列(鏈表)僚饭,Looper.loop()
循環(huán)從消息隊列里取出消息震叮,又將消息分發(fā)給 Handler 去處理。通過這個 target 我們也可以知道一個小細節(jié)鳍鸵,Handler 只能處理自己所發(fā)出的消息苇瓣。
0x04
理解清楚之后我們跟著順序,看看 Handler 是如何處理和分發(fā)消息的偿乖。
// 處理消息方法击罪,交給子類復寫
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
我們看到 dispatchMessage()
只是一個分發(fā)方法哲嘲,如果 Runnable 類型的 callback 為空,則執(zhí)行 handleMessage(msg)
處理信息媳禁,該方法為空眠副,是交給子類進行復寫,并且執(zhí)行線程是在 Handler 所創(chuàng)建的線程竣稽。
如果 callback 不為空囱怕,則會執(zhí)行 handleCallback(msg)
來處理信息,該方法會調(diào)用 callback 的 run()
方法毫别。
其實說簡單一點娃弓,就是 Handler 的兩種分發(fā)類型。
一種是 post(r)
另一種是 sendMessage(msg)
岛宦。
我們具體看看這兩個方法:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 與當前 Handler 綁定
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
做了一個導圖台丛,方便理解下:
從中我們可以看到,在 post(r)
時砾肺,會將 Runnable 包裝成 Message 對象挽霉,并且賦值給 Message 的 callback 字段,最后跟 sendMessage(msg)
方法一樣將消息插入隊列变汪。
根據(jù)代碼和導圖炼吴,無論是 post(r)
還是 sendMessage(msg)
都會最終調(diào)用 sendMessageAtTime(msg,time)
總結(jié)
Handler 最終將消息追加到 MessageQueue 中,而 Looper 不斷的從 MessageQueue 中讀取消息疫衩,并且調(diào)用 Handler 的 dispatchMessage 分發(fā)消息硅蹦,最后交給上層處理消息。