在剛學(xué)習(xí)Android網(wǎng)絡(luò)請求時(shí)壶笼,肯定會接觸到UI阻塞,然后認(rèn)識了Handler,Handler是我們最常用的解決方案之一逝段。
簡單點(diǎn)說Handler其實(shí)就是解決多線程通信的一個(gè)東西,那它是怎么具體工作的呢?Looper和MessageQueue又是什么青团,和Handler又和什么關(guān)聯(lián)呢?讓我們來慢慢分析咖楣。
首先我們先來看看主線程是怎么工作的督笆,我們知道在Android啟動時(shí),會默認(rèn)有一個(gè)主線程(UI線程)诱贿,在主線程中會關(guān)聯(lián)一個(gè)MessageQueue,所有操作都會封裝成一個(gè)消息來由主線程處理娃肿。為了保證主線程不會主動退出,主線程會把所有獲得消息的操作放在一個(gè)死循環(huán)中珠十,這樣主線程就會一直處理消息料扰,整個(gè)系統(tǒng)通信也就跑起來了,如下圖:
主線程的Looper是怎么自動創(chuàng)建的焙蹭?
主線程是在ActivityThread.main方法中創(chuàng)建的晒杈,ActivityThread.main是應(yīng)用程序的入口。
部分源代碼:
…
Process.setArgV0("<pre-initialized>");
//創(chuàng)建Looper對象
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//執(zhí)行消息循環(huán)壳嚎,讓MessageQueue動起來
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
…
執(zhí)行完ActivityThread.main方法桐智,應(yīng)用程序就啟動起來了,并且會一直從消息隊(duì)形中存消息烟馅、取消息然后處理消息说庭,這樣整個(gè)系統(tǒng)就轉(zhuǎn)起來了。
Handler郑趁、Looper刊驴、MessageQueue之間的對應(yīng)關(guān)系?
由于主線程阻塞超過5秒會ANR,所以一些耗時(shí)任務(wù)都是交給子線程來處理的捆憎,子線程處理好后想要交給主線程更新UI,我們知道子線程是不可以更新UI的舅柜,一般我們最常用的手段就是Handler,Handler從子線程拿到的數(shù)據(jù)封裝成消息Post到主線程,然后Handler再在HandleMessage方法中處理更新UI躲惰,但是Handle必須在主線程中創(chuàng)建致份!為什么Handle必須在主線中創(chuàng)建呢?因?yàn)橐粋€(gè)Handler對應(yīng)一個(gè)MessageQueue,而MessageQueue被封裝在Looper中础拨,所以說一個(gè)Handler對應(yīng)一個(gè)Looper,而Looper又被ThreadLocal封裝在Thread中氮块,最終每個(gè)消息隊(duì)列會關(guān)聯(lián)一個(gè)線程。默認(rèn)情況下诡宗,消息隊(duì)列只有一個(gè)就是在主線程消息隊(duì)列滔蝉,即在ActivityThread.main方法中創(chuàng)建的,最后啟用Looper.loop來執(zhí)行循環(huán)塔沃。
Handler是如何關(guān)聯(lián)MessageQueue和線程的呢蝠引?
我們來看看Handler部分源碼:
public Handler()
{
…
mLooper = Looper.myLooper(); //獲取Looper對象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //獲取MessageQueue對象
mCallback = callback;
mAsynchronous = async;
…
}
在Handler的構(gòu)造方法中,用Looper.myLooper獲得Looper對象蛀柴,又從Looper對象中獲得MessageQueue對象螃概,那么Looper.myLooper()又是怎么工作的呢?
Hoopler類部分源代碼:
//為當(dāng)前線程設(shè)置一個(gè)Looper
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));
}
//創(chuàng)建主線程Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
myLooper方法通過sThreadLocal.get()來獲取的名扛,prepare()方法中sThreadLocal設(shè)置了Looper對象谅年,這樣Looper就和Handler關(guān)聯(lián)起來了,即Handler和線程也就關(guān)聯(lián)起來了肮韧。
看到這里也就解釋了為什么Handler只能在主線程中創(chuàng)建了融蹂,因?yàn)镠andler要與主線程關(guān)聯(lián),這樣才能在HandlerMessage方法中更新UI弄企,此時(shí)超燃,更新UI才是安全的。
Looper是如何執(zhí)行消息循環(huán)的拘领?
消息循環(huán)的建立是通過Looper.loop()方法意乓,部分源碼:
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(); //從隊(duì)列中獲取消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//把消息傳給Handler
msg.target.dispatchMessage(msg);
//回收消息
msg.recycleUnchecked();
}
}
Loop()方法其實(shí)是一個(gè)死循環(huán),不斷地從中取出消息仍給Handler约素。
我們總結(jié)一下Looper,通過 Looper.prepare()獲得對象届良,然后通過Looper.loop()不斷循環(huán),這兩步是成對出現(xiàn)的圣猎。
消息處理的機(jī)制士葫?
msg.target.dispatchMessage(msg);這句是loop()方法中處理的方法,那它是怎么工作的呢送悔?target是一個(gè)Handler類型慢显,target通過Handler投遞到MessageQueue然后又分發(fā)到Handler處理爪模,整個(gè)流程走一遍,下面再看看dispatchMessage()方法源碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
從中可以看到dispatchMessage()方法只是一個(gè)分發(fā)方法 荚藻,如果Runable類型的callback為空屋灌,則執(zhí)行handlerMessage(),我們會將更新UI的代碼寫在這個(gè)方法中。當(dāng)callback不為空時(shí)应狱,會執(zhí)行handCallback來處理共郭,該方法會調(diào)用callback的run方法,其實(shí)這是Handler分發(fā)的兩種類型侦香,比如post(Runable callback)則callback不會為空落塑,而sendMessage()一般不會設(shè)置callback纽疟,因此執(zhí)行handMessage()這個(gè)分支罐韩,源碼如下:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在上述程序中看到,post()方法中會把Runable包裝成一個(gè)Message對象然后調(diào)用sendMessageDelayed()方法插入到MessageQueue污朽。
總結(jié)
Handler通過Looper.myLooper()拿到Looper實(shí)例散吵,又通過Looper實(shí)例拿到MessageQuenu實(shí)例,這樣Handler就與線程建立聯(lián)系了蟆肆,Handler把子線程的消息Post到MessageQueue,消息在MessageQueue循環(huán),Looper利用dispatchMessage分發(fā)消息矾睦,handlerMessage或者callback方法拿回消息進(jìn)行更新UI.