對Android開發(fā)來說Handler機制是很重要的也是面試必問的一點,相信大家也都能說出它的基本運行機制。
由
Handler,Looper,Message,MessageQueue幾部分組成,當(dāng)Handler通過sendMessage()或postRunnable()將Message添加到MessageQueue中,再由Looper.loop()從MessageQueue中取出交由Handler處理。Looper.loop()就是個死循環(huán),一直在從MessageQueue中獲取Message。
這是我最初入行時在面試中的回答,你說它不對吧,也就那么回事。你說它對吧,又遠不夠詳細。比如:
- Handler是如何實現(xiàn)跨線程操作?
- 既然Looper.loop()是死循環(huán),為什么沒有占用大量的cpu消耗呢?
- 為什么主線程不會因Looper.loop()里的死循環(huán)卡死?
這篇先來看看第一個問題:
Handler是如何實現(xiàn)跨線程操作?
我們先來看看通過Handler發(fā)送到MessageQueue的message是如何被Looper.loop()拿出來去處理的。
public static void loop() {
// 獲取當(dāng)前線程的looper對象,沒有則拋出異常
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
me.mInLoop = true;
// 獲取looper中的MessageQueue對象
final MessageQueue queue = me.mQueue
// 開始循環(huán)
for (;;) {
// 從queue中獲取message,next()里面也是死循環(huán),當(dāng)沒有消息時會阻塞,
// 調(diào)用MessageQueue.quit()后會清空隊列中消息并返回null,此時looper也會跳出死循環(huán),中止loop
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
...
try {
// msg.target就是message中綁定的Handler對象,通過調(diào)用Handler的dispatchMessage(msg)來真正處理message
// 不管是sendMessage還是postRunnable都會在最終將message插入隊列時為message綁定handler
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
...
} finally {
...
}
...
}
}
public void dispatchMessage(@NonNull Message msg) {
// 優(yōu)先使用message中的callback來處理
if (msg.callback != null) {
handleCallback(msg);
} else {
// 如果message沒有設(shè)置callback,則使用handler中的callback來處理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 兩個callback都沒有設(shè)置的情況下則調(diào)用handleMessage()來處理
handleMessage(msg);
}
}
從上述代碼中我們可以看出是在loop()中通過queue.next()取出message,再調(diào)用message中綁定的handler的dispatchMessage()去處理message。
既然如此,那是否就是Looper.loop()在哪個線程調(diào)用,dispatchMessage()或者說最終message的處理就在哪個線程被執(zhí)行?
那Looper.loop()又是在哪里被執(zhí)行的呢?
上面我們看到了在loop()中第一步就是通過myLooper()獲取Looper對象,再從looper對象中獲取到對應(yīng)的MessageQueue,我們來看一看myLooper()的實現(xiàn).
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
可以看到它只是很簡單的使用sThreadLocal.get()獲取Looper對象而已,那ThreadLocal又是什么呢?簡單來說就是每個線程對其進行訪問時都是線程自己的變量,或者說它對每個線程都有自己的獨立空間來存儲數(shù)據(jù)。我們不對他做詳細解釋,可以參考官方注釋:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
我們接著來看,可以看到sThreadLocal的set()是在prepare()中被調(diào)用,它也是靜態(tài)方法。那就是說我們可以直接通過Looper.prepare()去為當(dāng)前線程(方法執(zhí)行時的線程)構(gòu)造Looper
對象,并且它會存儲在Looper中的sThreadLocal變量中,同時會在初始化前做校驗來保證每個線程只能初始化一次。而ThreadLocal的特性保證了我們在任何地方調(diào)用myLooper()都會獲取到當(dāng)前線程對應(yīng)的Looper對象。
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我們再回到問題本身,答案已經(jīng)明了,我們在創(chuàng)建Handler時傳入了與線程綁定的Looper對象,因為最終對message的處理是在Looper.loop()中完成的,所以通過Handler發(fā)送的message消息都會在Looper對象對應(yīng)的線程中執(zhí)行,其實與Handler創(chuàng)建的線程無關(guān)(當(dāng)你為Handler傳入looper對象時)。
例如,在A線程中創(chuàng)建Handler對象h并傳入A線程的Looper對象,并調(diào)用了loop(),那么你在任意線程使用h對象發(fā)送的message最終都會在A線程被處理。因為你發(fā)送的Message會在被A線程執(zhí)行的loop()內(nèi)取出并處理。
如有錯誤,歡迎大家評論里指正,不勝感激。