Handler在Android中負(fù)責(zé)調(diào)度消息并將來(lái)某個(gè)時(shí)段處理消息唐责。Android有大量的消息驅(qū)動(dòng)方式來(lái)進(jìn)行交互,比如四大組件的的啟動(dòng)過(guò)程的交互,都離不開消息機(jī)制泪蔫。
消息機(jī)制涉及MessageQueue/Message/Looper/Handler這4個(gè)類。
Message:消息分為硬件產(chǎn)生的消息(如按鈕喘批、觸摸)和軟件生成的消息撩荣;
MessageQueue:消息隊(duì)列的主要功能向消息池投遞消息和取走消息池的消息铣揉;
Handler:消息輔助類,主要功能向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage)餐曹;
Looper:不斷循環(huán)執(zhí)行(Looper.loop)逛拱,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者。
梳理過(guò)程從handler構(gòu)造函數(shù)開始台猴,然后產(chǎn)生一條消息通過(guò)sendMessage向下分發(fā)朽合。之后又是如何被調(diào)度,最后被怎樣處理卿吐。
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這里是通過(guò)Looper中的myLooper方法來(lái)獲得Looper實(shí)例的旁舰,如果Looper為null的話就會(huì)拋異常,拋出的異常內(nèi)容翻譯過(guò)來(lái)就是無(wú)法在未調(diào)用 Looper.prepare()的線程內(nèi)創(chuàng)建handler嗡官,new handler的時(shí)候沒(méi)有報(bào)錯(cuò)那么就一定之前就調(diào)用了 Looper.Prepare()箭窜,在哪里創(chuàng)建的呢?
答案在 ActivityThread.main() 中衍腥。
ActivityThread 的 main()方法就是整個(gè)APP的入口磺樱,也就是我們通常所講的主線程, UI線程婆咸。但他實(shí)際上并不是一個(gè)線程竹捉,ActivityThread 并沒(méi)有繼承 Thread 類,我們可以把他理解為主線程的管理者尚骄,負(fù)責(zé)管理主線程的執(zhí)行块差,調(diào)度等操作,看下main方法的源碼倔丈。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
...
}
兩個(gè)關(guān)鍵方法 Looper.prepareMainLooper()和Looper.loop()憨闰,loop方法后面再分析,繼續(xù)看prepareMainLooper的方法內(nèi)部如下:
// 1
public static void prepareMainLooper() {
// 2
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 2
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 3
sThreadLocal.set(new Looper(quitAllowed));
}
// 3
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
到1??這里就解決了需五,為什么我們?cè)谥骶€程中使用Handler之前沒(méi)有調(diào)用Looper.prepare方法的問(wèn)題了鹉动,就是在這里調(diào)用了prepare 方法。
2??prepare方法內(nèi)部把 Looper 放到ThreadLocal中與當(dāng)前線程綁定宏邮,保證一個(gè)線程只有一個(gè)Looper實(shí)例泽示,同時(shí)一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue。
3??在Looper的構(gòu)造函數(shù)中始化MessageQueue
所以初始化Handler之前Looper 和 MessageQueue就已經(jīng)初始化了,并把Looper放到ThreadLocal中蜜氨。ThreadLocal有個(gè)特點(diǎn)是你set進(jìn)去的值是以當(dāng)前所處的線程為key械筛,value就是你set的值。源碼如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Handler的sendMessage方法都做了什么
先看一下sendMessage的流程圖:
簡(jiǎn)單過(guò)一遍飒炎,sendXXX 這些方式最終還是會(huì)調(diào)用到 enqueueMessage 這個(gè)方法上來(lái)的邏輯最后調(diào)用queue.enqueueMessage 添加消息到消息隊(duì)列MessageQueue中变姨,下面就是消息的入隊(duì)邏輯
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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;
}
}
return true;
}
消息入隊(duì)順序是按照 Message 觸發(fā)時(shí)間 long when入隊(duì)的,有新消息加入時(shí)厌丑,會(huì)循環(huán)遍歷當(dāng)前消息隊(duì)列定欧,對(duì)比消息觸發(fā)時(shí)間,直到找到當(dāng)前消息合適的插入位置怒竿,以此來(lái)保證所有消息的觸發(fā)時(shí)間順序砍鸠。即 MessageQueue 添加消息到消息隊(duì)列中。
所以消息的存儲(chǔ)與管理MessageQueue 來(lái)負(fù)責(zé)
誰(shuí)負(fù)責(zé)把消息分發(fā)出去
Handler把Message交給MessageQueue并存儲(chǔ)起來(lái) 耕驰,那么誰(shuí)來(lái)負(fù)責(zé)把我這里的消息分發(fā)出去并且告訴主線程的人(Handler)呢爷辱?答案就是 Looper 了!
之前分析的ActivityThread.main()中的Looper.loop還沒(méi)有講到我們接著跟進(jìn)去看一下源碼
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
// 循環(huán)調(diào)用 MessageQueue 的 next 方法獲取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
loop()不斷從MessageQueue中取消息朦肘,把消息交給target(handler)的dispatchMessage方法處理饭弓。下面是dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到dispatchMessage最后調(diào)用了handleMessage方法,在源碼里面是個(gè)空實(shí)現(xiàn)媒抠。需要我們?cè)趧?chuàng)建handler的時(shí)候復(fù)寫弟断,根據(jù)不同的message處理不同的業(yè)務(wù)邏輯。
整個(gè)Handler的消息分發(fā)調(diào)度流程就講完了趴生,為了增強(qiáng)記憶阀趴,我再把整個(gè)流程拼成一塊如下圖: