這是“Android訊息機制”系列的第三篇文章,系列文章目錄如下:
訊息機制的故事
壽司
陳放在壽司碟
上,壽司碟
按先後順序被排成佇列
送上傳送帶
。傳送帶
被啟動後,壽司
挨個呈現到你面前,你有三種享用壽司的方法。
將Android概念帶入後,就變成了Android訊息機制的故事:
-
壽司碟 —>
訊息(Message)
-
佇列 —>
訊息佇列(MessageQueue)
-
傳送帶 —>
訊息泵 (Looper)
-
壽司 —> 你關心的資料
-
享用壽司方法 —> 處理資料方式
這一篇分析下處理訊息的三種方式。
#處理訊息的起點
先回憶一下分發訊息的關鍵函式Looper.loop()
,原始碼如下:
//省略了非關鍵程式碼
public static void loop()
{
...
//拿訊息的無限迴圈
for (; ; )
{
//從隊頭拿訊息
Message msg = queue.next(); // might block
...
//分發訊息
msg.target.dispatchMessage(msg);
...
}
複製程式碼
還記得系列文章第一篇中留下的懸念嗎?在構造訊息時,為啥訊息物件持有構造它的Handler物件?現在可以回答這個問題了:
Looper
遍歷訊息時,把訊息交給與其對應的Handler
處理。交接訊息是通過呼叫Handler.dispatchMessage()
,這是訊息分發的終點,也是處理訊息的起點。
處理訊息的方式
移步到Handler.dispatchMessage()
:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//處理方式1
if (msg.callback != null) {
handleCallback(msg);
} else {
//處理訊息方式2
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//處理訊息方式3
handleMessage(msg);
}
}
複製程式碼
- 可以清楚的看到有三種處理訊息的方式
- 直接執行
Message
中Runnable.run()
。
public class Handler{
...
private static void handleCallback(Message message) {
message.callback.run();
}
...
}
public final class Message implements Parcelable {
...
/*package*/ Runnable callback;
...
}
複製程式碼
Message
的Runnable
是哪來的?
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//將 Runnable 包裝成 Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製程式碼
每次呼叫Handler.post(Runnable r)
時,Handler
都會將Runnable
包裝成Message
,這樣Runnable
和Message
就可以共用訊息分發邏輯,但它們的處理邏輯會有所不同,如果訊息中帶有Runnable
則會最優先被處理,處理方式是直接呼叫Runnable.run()
Handler.Callback
方式
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
複製程式碼
除了繼承Handler
這種最常見的處理訊息方式外,我們還可以通過Handler.Callback
來定義處理訊息的方式:
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won`t be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
複製程式碼
- 過載
handleMessage()
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
複製程式碼
通常我們是通過過載這個函式來定義處理訊息的方式。
總結
Android訊息機制共有三種訊息處理方式,它們是互斥的,優先順序從高到低分別是1. Runnable.run()
2. Handler.callback
3. 過載Handler.handleMessage()