回轉壽司你一定吃過!——Android訊息機制(處理)

Taylor發表於2019-02-15

這是“Android訊息機制”系列的第三篇文章,系列文章目錄如下:

  1. 回轉壽司你一定吃過!——Android訊息機制(構造)
  2. 回轉壽司你一定吃過!——Android訊息機制(分發)
  3. 回轉壽司你一定吃過!——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);
        }
    }
複製程式碼
  • 可以清楚的看到有三種處理訊息的方式
  1. 直接執行MessageRunnable.run()
public class Handler{
     ...
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    ...
}

public final class Message implements Parcelable {
     ...
     /*package*/ Runnable callback;
    ...
}
複製程式碼

MessageRunnable是哪來的?

    /**
     * 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,這樣RunnableMessage就可以共用訊息分發邏輯,但它們的處理邏輯會有所不同,如果訊息中帶有Runnable則會最優先被處理,處理方式是直接呼叫Runnable.run()

  1. 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);
    }
複製程式碼
  1. 過載handleMessage()
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
複製程式碼

通常我們是通過過載這個函式來定義處理訊息的方式。

總結


Android訊息機制共有三種訊息處理方式,它們是互斥的,優先順序從高到低分別是1. Runnable.run() 2. Handler.callback 3. 過載Handler.handleMessage()

相關文章