老司機們都知道,Android的執行緒間通訊就靠Handler、Looper、Message、MessageQueue這四個麻瓜兄弟了,那麼,他們是怎麼運作的呢?下面我們來分析一下
Looper(先分析這個是因為能夠引出四者的關係)
在Looper中,維持一個Thread
物件以及MessageQueue
,通過Looper的建構函式我們可以知道:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//傳入的引數代表這個Queue是否能夠被退出
mThread = Thread.currentThread();
}
複製程式碼
Looper
在建構函式裡幹了兩件事情:
- 將執行緒物件指向了建立
Looper
的執行緒 - 建立了一個新的
MessageQueue
分析完建構函式之後,接下來我們主要分析兩個方法:
looper.loop()
looper.prepare()
looper.loop()(在當前執行緒啟動一個Message loop機制,此段程式碼將直接分析出Looper、Handler、Message、MessageQueue的關係)
public static void loop() {
final Looper me = myLooper();//獲得當前執行緒繫結的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲得與Looper繫結的MessageQueue
// 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();
//進入死迴圈,不斷地去取物件,分發物件到Handler中消費
for (;;) {
Message msg = queue.next(); // 不斷的取下一個Message物件,在這裡可能會造成堵塞。
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);
}
//在這裡,開始分發Message了
//至於這個target是神馬?什麼時候被賦值的?
//我們一會分析Handler的時候就會講到
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);
}
//當分發完Message之後,當然要標記將該Message標記為 *正在使用* 啦
msg.recycleUnchecked();
}
}
複製程式碼
分析了上面的原始碼,我們可以意識到,最重要的方法是:
queue.next()
msg.target.dispatchMessage(msg)
msg.recycleUnchecked()
其實Looper中最重要的部分都是由Message
、MessageQueue
組成的有木有!這段最重要的程式碼中涉及到了四個物件,他們與彼此的關係如下:
MessageQueue
:裝食物的容器Message
:被裝的食物Handler
(msg.target實際上就是Handler
):食物的消費者Looper
:負責分發食物的人
looper.prepare()(在當前執行緒關聯一個Looper物件)
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在當前執行緒繫結一個Looper
sThreadLocal.set(new Looper(quitAllowed));
}
複製程式碼
以上程式碼只做了兩件事情:
- 判斷當前執行緒有木有
Looper
,如果有則丟擲異常(在這裡我們就可以知道,Android規定一個執行緒只能夠擁有一個與自己關聯的Looper
)。 - 如果沒有的話,那麼就設定一個新的
Looper
到當前執行緒。
Handler由於我們使用Handler的通常性的第一步是:
Handler handler = new Handler(){
//你們有沒有很好奇這個方法是在哪裡被回撥的?
//我也是!所以接下來會分析到喲!
@Override
public void handleMessage(Message msg) {
//Handler your Message
}
};
複製程式碼
所以我們先來分析Handler
的構造方法
//空引數的構造方法與之對應,這裡只給出主要的程式碼,具體大家可以到原始碼中檢視
public Handler(Callback callback, boolean async) {
//列印記憶體洩露提醒log
....
//獲取與建立Handler執行緒繫結的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取與Looper繫結的MessageQueue
//因為一個Looper就只有一個MessageQueue,也就是與當前執行緒繫結的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
帶上問題:
Looper.loop()
死迴圈中的msg.target
是什麼時候被賦值的?handler.handleMessage(msg)
在什麼時候被回撥的?
A1:Looper.loop()
死迴圈中的msg.target
是什麼時候被賦值的?
要分析這個問題,很自然的我們想到從傳送訊息開始,無論是handler.sendMessage(msg)
還是handler.sendEmptyMessage(what)
,我們最終都可以追溯到以下方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//引用Handler中的MessageQueue
//這個MessageQueue就是建立Looper時被建立的MessageQueue
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//將新來的Message加入到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
複製程式碼
我們接下來分析enqueueMessage(queue, msg, uptimeMillis)
:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//顯而易見,大寫加粗的賦值啊!
**msg.target = this;**
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
A2:handler.handleMessage(msg)
在什麼時候被回撥的?
通過以上的分析,我們很明確的知道Message
中的target
是在什麼時候被賦值的了,我們先來分析在Looper.loop()
中出現過的過的dispatchMessage(msg)
方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//看到這個大寫加粗的方法呼叫沒!
**handleMessage(msg);**
}
}
複製程式碼
加上以上分析,我們將之前分析結果串起來,就可以知道了某些東西:
Looper.loop()
不斷地獲取MessageQueue
中的Message
,然後呼叫與Message
繫結的Handler
物件的dispatchMessage
方法,最後,我們看到了handleMessage
就在dispatchMessage
方法裡被呼叫的。
通過以上的分析,我們可以很清晰的知道Handler、Looper、Message、MessageQueue這四者的關係以及如何合作的了。
總結:
當我們呼叫handler.sendMessage(msg)
方法傳送一個Message
時,實際上這個Message
是傳送到與當前執行緒繫結的一個MessageQueue
中,然後與當前執行緒繫結的Looper
將會不斷的從MessageQueue
中取出新的Message
,呼叫msg.target.dispathMessage(msg)
方法將訊息分發到與Message
繫結的handler.handleMessage()
方法中。
一個Thread
對應多個Handler
一個Thread
對應一個Looper
和MessageQueue
,Handler
與Thread
共享Looper
和MessageQueue
。
Message
只是訊息的載體,將會被髮送到與執行緒繫結的唯一的MessageQueue
中,並且被與執行緒繫結的唯一的Looper
分發,被與其自身繫結的Handler
消費。
- Enjoy Android :) 如果有誤,輕噴,歡迎指正。