handler 是android SDK 提供給開發者方便進行異步消息處理的類,而我們熟悉的AsyncTack、retrofit內部都是用了handler,加以巧妙的封裝。由此看來handler似乎比我們想象的更重要。
進入正題,講解分爲三部分:機制說明、源碼分析、總結。
一: handler的機制說明
根據上圖我們理下邏輯:
首先主線程中會自動創建一個looper,而looper構造方法中會創建MessageQueue。而我們在主線程創建Handler的時候會取出當前線程中的looper。然後通過looper不斷的讀MessageQueue。looper讀到Message消息後就會回調handler的disapatchMessage方法。而handler發送消息就是在MessageQueue中添加Message。
二、源碼解析
2.1Handler的構造函數:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
1)我們可以看到Handler的構造函數中,有個looper的創建。Looper.myLooper()是獲取當前線程的Looper。也就是說要使用Handler就必須當前線程有Looper對象。
2)mQueue即MessageQueue的實體類,我們可以看到mQueue是在looper內的。賦值給mQueue就是爲了looper和Handler共用一個消息隊列。
而Handler 中的sendEmptyMessage之類的方法最終都會走到enqueueMessage方法中,讓我們來看下源碼:
2.2Handler的enqueueMessage:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
1)msg.target = this;中this指的是Handler本身。
2)queue.enqueueMessage(msg, uptimeMillis);既是將msg加入到MessageQueue的列表隊列中。
MessageQueue中的enqueueMessage(msg, uptimeMillis)即插入Message的方法,還有next()即讀取並刪除Message方法。這兩個方法是MessageQueue的最核心的兩個方法。
現在我們知道Handler是怎麼發生消息到達MessageQueue中,那麼MessageQueue中的Message又是怎麼被提取出來? 是通過Lopper的管理,那麼現在我們來看看來Lopper的源碼:
2.3Looper的prepare(boolean quitAllowed):
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));
}
prepare(boolean quitAllowed)這個方法是looper使用前必須調用的方法,由此代碼可以看出Lopper是通過sThreadLocal去添加管理looper。通過sThreadLocal 保存的話可以確保我們每個線程獲取的looper都是唯一的。sThreadLocal在很多地方都叫做線程本地變量,他能爲每個變量在每個線程都創建一個副本,那麼每個線程都可以訪問自己內部副本的變量。這樣就不會造成線程數據不一致的現象。
這個方法首先是判斷sThreadLocal是否爲空,不爲空即表示已創建好不必再做操作。若爲空則通過sThreadLocal的set方法創建好這個線程的looper對象放到容器當中。也就是說prepare方法就相當於是Looper的創建方法。
2.4Looper的myLooper()方法:
public static Looper myLooper() {
return sThreadLocal.get();
}
myLooper方法在Handler的構造方法中調用了,我們看源碼其實很簡單,直接在sThreadLocal中獲取當前線程中的Looper。而sThreadLocal中的Looper就是上面prepare方法中添加進去的。
ThreadLocal這個容器是線程所持有的,所以能夠保證我們的looper是和其他的線程獨立開來的。我們用get方法就可以獲得相應的Looper對象。
2.5Looper的構造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
1)在創建looper 對象的時候會創建MessageQueue對象。
看完這裏我們可以知道MessageQueue是愛Looper中的變量,而Message的target變量會指向Handler,從而使得MessageQueue、Looper和Handler三者關聯。
那麼Looper是怎麼去管理輪詢MessageQueue的呢,下面我們來看Looper的loop方法:
2.6Looper的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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
我們可以看到loop也是通過myLooper()方法獲取當前線程的looper。然後判斷是否爲空,我們看如果爲空,會提示先調用Looper.prepare()方法。
判斷完後給MessageQueue賦值。
而後有一個死循環,不斷地從MessageQueue中獲取消息,如果消息不爲空則調用到msg.target.dispatchMessage(msg);這個方法,我們在看Handler源碼的時候看到message.target這個變量是指向Handler。也就是說調用了handler的dispatchMessage方法。
那麼我們就需要看下Handler的這個dispatchMessage源碼是怎麼處理的。
2.6Handler的dispathchMessage方法 :
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();
}
public interface Callback {
public boolean handleMessage(Message msg);
}
從源碼可以看到,msg先是判斷callback是否爲空,那麼callback是什麼?callback就是Runnable,如果不爲空,那麼就會調用handleCallback方法去執行Runnable的run方法。如果爲空就會去回調一個接口方法handleMessage。這個就是我們在寫Handler 的時候去重寫的handleMessage的方法。
那麼從源碼上已經查看完了handler的發送消息到Looper的MessageQueue輪詢。覺得需要重新理下邏輯的可以重新看下最上面的 handler機制說明。
三:總結
looper:
- 每個線程只有一個looper。(looper實體 會存儲在Looper的ThreadLocal的實體變量中,通過Looper的myLooper() 方法獲取)
- 負責不斷地讀取MessageQueue隊列裏的消息 (Looper的 loop方法)
- 構造方法中會創建MessageQueue(Looper的prepare()方法中會創建Looper)
MessageQueue:
- 存放Message的對象(Message可放Runnable或其他字符信息)
- 先進先出的方法管理
- MessageQueue是使用單列表存儲Message
Handler:
- 發送消息(所有發送消息的方法最終都會調用enqueueMessage方法,將msg放入到MessageQueue的列表中)
- 處理消息(Handler 的dispatchMessage方法,如果是有 runnable就會執行,沒有的話就交到Handler的handleMessage方法中,一般我就是重新這個方法去處理 message)
- Handler需要正常工作需要有個looper對象(平時我們在主線程中不需要處理loop的準備,因爲主線程會自動創建looper。但是如果我們另起一個線程做異步操作的時候,直接用handler會報沒有loop的錯誤,我們需要在這個線程中先創建looper)
另外我們在子線程中使用Handler的時候必須自己調用Looper.prepare()和Looper.loop(),前者用於創建Looper,後者是啓動消息循環的查詢。
而Android也提供了HandlerThread類來供子線程使用Handler而不用自己再調用Looper的方法,HandlerThread其實就是將Looper的調用封裝在內部。讓開發者不用重複編寫調用Looper的方法。