【Android 系統開發】_「核心技術」篇 -- Handler機制(原理)

開篇

核心源碼

關鍵類 路徑
Looper.java frameworks/base/core/java/android/os/Looper.java
Message.java frameworks/base/core/java/android/os/Message.java
MessageQueue.java frameworks/base/core/java/android/os/MessageQueue.java

簡述

在整個 Android 的源碼世界裏,有兩大利劍,其一是 Binder 機制,另外一個即是 Handler 消息機制。消息機制涉及 MessageQueue/Message/Looper/Handler 這4個類。java

Handler 是 Android 中引入的一種讓開發者參與處理線程中消息循環的機制。咱們在使用 Handler 的時候與 Message 打交道最多,Message 是 Hanlder 機制向開發人員暴露出來的相關類,能夠經過 Message 類完成大部分操做 Handler 的功能。android

做爲一名程序員,咱們不只須要知道怎麼用 Handler ,還要知道其內部如何實現的,這就是我寫這篇文章的目的。git

模型

消息機制(Handler)主要包含:程序員

        ✨ Message:消息分爲硬件產生的消息(如按鈕、觸摸)和軟件生成的消息;
        ✨ MessageQueue:消息隊列的主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
        ✨ Handler:消息輔助類,主要功能向消息池發送各類消息事件(Handler.sendMessage)和處理相應消息事件(Handler.handleMessage);
        ✨ Looper:不斷循環執行(Looper.loop),按分發機制將消息分發給目標處理者。spring

實例

/*
 * A typical example of the implementation of a Looper thread,
 * using the separation of {@link #prepare} and {@link #loop} to create an
 * initial Handler to communicate with the Looper.
 */
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

接下來咱們就圍繞這個實例展開講解!安全

Looper

消息隊列 MessageQueue 只是存儲 Message 的地方,真正讓消息隊列循環起來的是 Looper,咱們先來重點分析 Looper。多線程

Looper 是用來使線程中的消息循環起來的。默認狀況下當咱們建立一個新的線程的時候,這個線程裏面是沒有消息隊列 MessageQueue 的。爲了可以讓線程可以綁定一個消息隊列,咱們須要藉助於 Looper :首先咱們要調用 Looper 的 prepare() 方法,而後調用 Looper 的 Loop() 方法。異步

須要注意的是 Looper.prepare() 和 Looper.loop() 都是在新線程的 run 方法內調用的,這兩個方法都是靜態方法。async

public static void prepare() {...}
    private static void prepare(boolean quitAllowed) {...}
    public static void loop() {...}

prepare()

咱們來看一下 Looper.prepare(),該方法是讓 Looper 作好準備,只有 Looper 準備好了以後才能調用 Looper.loop() 方法。ide

public static void prepare() {    
        prepare(true);     // 無參,調用 prepare(boolean quitAllowed)
    }
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
        // 每一個線程只容許執行一次該方法,第二次執行時已有 Looper,則會拋出異常!
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 建立 Looper 對象,並保存到當前線程的本地存儲區
        sThreadLocal.set(new Looper(quitAllowed));      
    }

上面的代碼首先經過 sThreadLocal.get() 拿到線程 sThreadLocal 所綁定的 Looper 對象,因爲初始狀況下 sThreadLocal 並無綁定 Looper ,因此第一次調用 prepare 方法時,sThreadLocal.get() 返回 null,不會拋出異常。

ThreadLocal

ThreadLocal:線程本地存儲區(Thread Local Storage,簡稱爲 TLS),每一個線程都有本身的私有的本地存儲區域,不一樣線程之間彼此不能訪問對方的TLS區域。

TLS 經常使用的操做方法:

set()

public void set(T value) {
        Thread t = Thread.currentThread();    // 獲取當前線程 
        ThreadLocalMap map = getMap(t);       //查找當前線程的本地儲存區
        if (map != null)
            map.set(this, value);             // 保存數據 value 到當前線程 this
        else
            createMap(t, value);
    }

咱們看下 getMap() 函數:

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

判斷 map 是否爲空,若是爲空則建立 ThreadLocalMap :

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

get()

public T get() {
        Thread t = Thread.currentThread();    // 獲取當前線程 
        ThreadLocalMap map = getMap(t);       // 查找當前線程的本地儲存區
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();   // 建立 ThreadLocalMap
    }

查看 setInitialValue() :

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

Looper 經過以下代碼保存了對當前線程的引用:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();    // sThreadLocal 爲 ThreadLocal 類型

因此在 Looper 對象中經過 sThreadLocal 就能夠找到其綁定的線程。ThreadLocal 中有個 set 方法和 get 方法,能夠經過 set 方法向 ThreadLocal 中存入一個對象,而後能夠經過 get 方法取出存入的對象。

ThreadLocal 在 new 的時候使用了泛型,從上面的代碼中咱們能夠看到此處的泛型類型是 Looper ,也就是咱們經過 ThreadLocal 的 set 和 get 方法只能寫入和讀取 Looper 對象類型。

構造函數

源碼中 Looper 的構造函數是 private 的,也就是在該類的外部不能用 new Looper() 的形式獲得一個 Looper 對象。

private Looper(boolean quitAllowed) {...}

咱們看下上面代碼中 new Looper() 建立 Looper 對象的工做:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 建立 MessageQueue 對象
    mThread = Thread.currentThread();        // 記錄當前線程
}

Looper.prepare()在每一個線程只容許執行一次,該方法會建立 Looper 對象,Looper 的構造方法中會建立一個 MessageQueue 對象,再將 Looper 對象保存到當前線程 TLS。

prepareMainLooper()

另外,與 prepare() 相近功能的,還有一個 prepareMainLooper() 方法,該方法主要在 ActivityThread 類中使用。

public static void prepareMainLooper() {
        prepare(false);          // 設置不容許退出的 Looper
        synchronized (Looper.class) {
            // 將當前的 Looper 保存爲主 Looper,每一個線程只容許執行一次
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

loop()

Looper.loop()的代碼以下:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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 對象中的消息隊列

        // 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 (;;) {     // 進入loop的主循環方法
            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
            // 默認爲null,可經過 setMessageLogging() 方法來指定輸出,用於 debug 功能
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);       // 用於分發 Message
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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();     // 確保分發過程當中 identity 不會損壞
            if (ident != newIdent) {
                // 打印 identity 改變的 log,在分發消息過程當中是不但願身份被改變的
            }

            msg.recycleUnchecked();   // 將 Message 放入消息池
        }
    }

咱們接下來會重點分析 loop() 裏面的幾個函數:

myLooper()

前面咱們說過,在執行完了 Looper.prepare() 以後,咱們就能夠在外部經過調用 Looper.myLooper() 獲取當前線程綁定的 Looper 對象。

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();      // 仍是經過 sThreadLocal.get()方法獲取當前線程綁定的 Looper 對象
    }

MessageQueue

// Looper 構造函數中建立了 mQueue,即 MessageQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 建立 MessageQueue 對象
    mThread = Thread.currentThread();        // 記錄當前線程
}
public static void loop() {
        final Looper me = myLooper();             // 獲取當前線程綁定的 Looper 對象
        if (me == null) {
            ... ...
        }
        final MessageQueue queue = me.mQueue;     // 獲取 Looper 對象中的消息隊列

變量 me 是經過靜態方法 myLooper() 得到的當前線程所綁定的 Looper,me.mQueue 就是當前線程所關聯的消息隊列。

for()

for (;;) {     // 進入loop的主循環方法

咱們發現for循環沒有設置循環終止的條件,因此這個for循環是個死循環。

Message

Message msg = queue.next(); // might block

咱們經過消息隊列 MessageQueue 的 next 方法從消息隊列中取出一條消息,若是此時消息隊列中有 Message,那麼 next 方法會當即返回該 Message,若是此時消息隊列中沒有 Message,那麼 next 方法就會阻塞式地等待獲取 Message。

dispatchMessage()

/*package*/ Handler target;
try {
    msg.target.dispatchMessage(msg);
    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
    if (traceTag != 0) {
        Trace.traceEnd(traceTag);
    }
}

msg 的 target 屬性是 Handler,該代碼的意思是讓 Message 所關聯的 Handler 經過 dispatchMessage 方法讓 Handler 處理該 Message ,關於 Handler 的 dispatchMessage 方法將會在下面詳細介紹。

recycleUnchecked()

msg.recycleUnchecked();    // 分發後的 Message 回收到消息池,以便重複利用

小結

loop()進入循環模式,不斷重複下面的操做,直到沒有消息時退出循環:

一、讀取 MessageQueue 的下一條 Message;
二、把 Message 分發給相應的 target;
三、再把分發後的 Message 回收到消息池,以便重複利用。

quit()

public void quit() {
        mQueue.quit(false);          // 消息移除
    }

    public void quitSafely() {
        mQueue.quit(true);           // 安全消息移除
    }

Looper.quit() 方法的實現最終調用的是 MessageQueue.quit() 方法。

MessageQueue.quit()

void quit(boolean safe) {
        if (!mQuitAllowed) {    當 mQuitAllowed 爲 false,表示不運行退出,強行調用 quit() 會拋出異常
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
消息退出的方式:

當 safe = true 時,只移除還沒有觸發的全部消息,對於正在觸發的消息並不移除
當 safe = flase 時,移除全部的消息

Handler

構造函數

無參構造

public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }
    
    public Handler(Callback callback, boolean async) {
        // 匿名類、內部類或本地類都必須申明爲static,不然會警告可能出現內存泄露
        if (FIND_POTENTIAL_LEAKS) {     // 默認爲 false
            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());
            }
        }

        // 必須先執行 Looper.prepare(),才能獲取 Looper 對象,不然爲null
        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;    // 設置消息是否爲異步處理方式
    }

對於 Handler 的無參構造方法,默認採用當前線程 TLS 中的 Looper 對象,而且 callback 回調方法爲 null,且消息爲同步處理方式。只要執行的 Looper.prepare() 方法,那麼即可以獲取有效的 Looper 對象。

有參構造

public Handler(Looper looper) {
        this(looper, null, false);
    }
    
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler 類在構造方法中,可指定 Looper,Callback 回調方法以及消息的處理方式(同步或異步),對於無參的 handler,默認是當前線程的 Looper。

dispatchMessage()

在 Looper.loop() 中,當發現有消息時,調用消息的目標 handler,執行 dispatchMessage() 方法來分發消息。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 當 Message 存在回調方法,回調 msg.callback.run() 方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // 當 Handler 存在 Callback 成員變量時,回調方法 handleMessage()
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // Handler 自身的回調方法 handleMessage()
            handleMessage(msg);
        }
    }

咱們須要重點分析下這個函數:

首先會判斷 msg.callback 存不存在,msg.callback 是 Runnable 類型,若是 msg.callback 存在,那麼說明該 Message 是經過執行 Handler 的 post() 系列方法將 Message 放入到消息隊列中的,這種狀況下會執行 handleCallback(msg)。

handleCallback

源碼以下:

private static void handleCallback(Message message) {
        message.callback.run();
    }

這樣咱們就清楚地看到咱們執行了 msg.callback 的 run 方法,也就是執行了 post() 所傳遞的 Runnable 對象的 run 方法。

mCallback

若是咱們不是經過 post() 系列方法將 Message 放入到消息隊列中的,那麼 msg.callback 就是 null ,代碼繼續往下執行。

接着咱們會判斷 Handler 的成員字段 mCallback 存不存在。mCallback 是 Hanlder.Callback 類型的,咱們在上面提到過,在 Handler 的構造函數中咱們能夠傳遞 Hanlder.Callback 類型的對象,該對象須要實現 handleMessage 方法,若是咱們在構造函數中傳遞了該 Callback 對象,那麼咱們就會讓 Callback 的 handleMessage 方法來處理 Message。

final Callback mCallback;
    
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

若是咱們在構造函數中沒有傳入 Callback 類型的對象,那麼 mCallback 就爲 null ,那麼咱們會調用 Handler 自身的 hanldeMessage 方法,該方法默認是個空方法,咱們須要本身重寫實現該方法。

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {      // 空函數
    }

綜上所述,咱們能夠看到 Handler 提供了三種途徑處理 Message ,並且處理有先後優先級之分:首先嚐試讓 post() 中傳遞的 Runnable 執行,其次嘗試讓 Handler 構造函數中傳入的 Callback 的 handleMessage 方法處理,最後纔是讓 Handler 自身的 handleMessage 方法處理Message。

Callback

Callback 是 Handle r中的內部接口,須要實現其內部的 handleMessage 方法,Callback 代碼以下:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

Handler.Callback 是用來處理 Message 的一種手段,若是沒有傳遞該參數,那麼就應該重寫 Handler 的 handleMessage 方法,也就是說爲了使得 Handler 可以處理 Message ,有兩種辦法:

  1. 向 Hanlder 的構造函數傳入一個 Handler.Callback 對象,並實現 Handler.Callback 的 handleMessage 方法
  2. 無需向 Hanlder 的構造函數傳入 Handler.Callback 對象,可是須要重寫 Handler 自己的 handleMessage 方法

也就是說不管哪一種方式,咱們都得經過某種方式實現 handleMessage 方法,這點與 Java 中對 Thread 的設計有殊途同歸之處。

在Java中,若是咱們想使用多線程,有兩種辦法:

1. 向 Thread 的構造函數傳入一個 Runnable 對象,並實現 Runnable 的 run 方法

2. 無需向 Thread 的構造函數傳入 Runnable 對象,可是要重寫 Thread 自己的 run 方法 

因此只要用過多線程 Thread,應該就對 Hanlder 這種須要實現 handleMessage 的兩種方式瞭然於心了。

在以前分析 Handler(用法篇)的時候咱們講到過兩種重要的方法:sendMessage() 和 post(),咱們從源碼角度進行進一步分析!

sendMessage

咱們看下 sendMessage() 源碼處理流程:

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

sendMessageDelayed

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終調 sendMessageAtTime()
    }

經過以上代碼能夠看書:sendMessage() 調用了 sendMessageDelayed() ,sendMessageDelayed() 又調用了 sendMessageAtTime()。

Handler 中還有 sendEmptyMessage() 方法:

public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終仍是要調 sendMessageAtTime()
    }

因而可知全部的 sendMessage 方法和 sendEmptyMessage 最終都調用了 sendMessageAtTime 方法。

post

咱們看下 post() 源碼處理流程:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

能夠看到內部調用了 getPostMessage 方法,該方法傳入一個 Runnable 對象,獲得一個 Message 對象。

getPostMessage

getPostMessage() 的源碼以下:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

經過上面的代碼咱們能夠看到在 getPostMessage 方法中,咱們建立了一個 Message 對象,並將傳入的 Runnable 對象賦值給 Message 的 callback 成員字段,而後返回該 Message ,而後在 post 方法中該攜帶有 Runnable 信息的 Message 傳入到 sendMessageDelayed 方法中。由此咱們能夠看到全部的 post 方法內部都須要藉助 sendMessage 方法來實現,因此 post() 與 sendMessage() 並非對立關係,而是 post() 依賴 sendMessage() ,因此 post() 方法能夠經過 sendMessage() 方法向消息隊列中傳入消息,只不過經過 post() 方法向消息隊列中傳入的消息都攜帶有 Runnable 對象(Message.callback)。

sendMessageAtTime

經過分別分析 sendEmptyMessage()、post() 方法與 sendMessage() 方法之間的關係,咱們能夠看到在 Handler 中全部能夠直接或間接向消息隊列發送 Message 的方法最終都調用了 sendMessageAtTime 方法,該方法的源碼以下:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

enqueueMessage

咱們發現 sendMessageAtTime() 方法內部調用了 enqueueMessage() 函數:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

咱們須要重點注意兩行代碼:

msg.target = this;     // 將 Message 的 target 綁定爲當前的 Handler
// 變量 queue 表示的是 Handler 所綁定的消息隊列 MessageQueue ,經過調用 queue.enqueueMessage(msg, uptimeMillis) 將 Message 放入到消息隊列中。
queue.enqueueMessage(msg, uptimeMillis);

還記得咱們以前在分析 Looper 的時候,最終提到的 dispatchMessage() 嗎?咱們回憶一下:

try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

MessageQueue

每一個線程內部都維護了一個消息隊列 —— MessageQueue。消息隊列 MessageQueue,顧名思義,就是存放消息的隊列。那隊列中存儲的消息是什麼呢?

假設咱們在UI界面上單擊了某個按鈕,而此時程序又剛好收到了某個廣播事件,那咱們如何處理這兩件事呢?由於一個線程在某一時刻只能處理一件事情,不能同時處理多件事情,因此咱們不能同時處理按鈕的單擊事件和廣播事件,咱們只能挨個對其進行處理,只要挨個處理就要有處理的前後順序。

爲此Android把UI界面上單擊按鈕的事件封裝成了一個 Message ,將其放入到 MessageQueue 裏面去,即將單擊按鈕事件的 Message 入棧到消息隊列中,而後再將廣播事件的封裝成以 Message ,也將其入棧到消息隊列中。

也就是說一個 Message 對象表示的是線程須要處理的一件事情,消息隊列就是一堆須要處理的 Message 的池。線程 Thread 會依次取出消息隊列中的消息,依次對其進行處理。

MessageQueue 中有兩個比較重要的方法,一個是 enqueueMessage 方法,一個是 next 方法。enqueueMessage 方法用於將一個 Messag e放入到消息隊列 MessageQueue 中,next 方法是從消息隊列 MessageQueue 中阻塞式地取出一個 Message。在 Android 中,消息隊列負責管理着頂級程序對象(Activity、BroadcastReceiver等)以及由其建立的全部窗口。

建立MessageQueue

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        // 經過 native 方法初始化消息隊列,其中 mPtr 是供 native 代碼使用
        mPtr = nativeInit();
    }

next()

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {     // 當消息循環已經退出,則直接返回
            return null;
        }

        // 循環迭代的首次爲 -1
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            // 阻塞操做,當等待 nextPollTimeoutMillis 時長,或者消息隊列被喚醒,都會返回
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 當消息 Handler 爲空時,查詢 MessageQueue 中的下一條異步消息 msg,則退出循環
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 當異步消息觸發時間大於當前時間,則設置下一次輪詢的超時時長
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 獲取一條消息,並返回
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        // 設置消息的使用狀態,即 flags |= FLAG_IN_USE
                        msg.markInUse();
                        // 成功地獲取 MessageQueue 中的下一條即將要執行的消息
                        return msg;
                    }
                } else {
                    // 沒有消息
                    nextPollTimeoutMillis = -1;
                }

                // 消息正在退出,返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 當消息隊列爲空,或者是消息隊列的第一個消息時
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                // 沒有 idle handlers 須要運行,則循環並等待
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 只有第一次循環時,會運行 idle handlers,執行完成後,重置 pendingIdleHandlerCount 爲 0
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // 去掉 handler 的引用

                boolean keep = false;
                try {
                    keep = idler.queueIdle();   // idle 時執行的方法
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 重置 idle handler 個數爲 0,以保證不會再次重複運行
            pendingIdleHandlerCount = 0;

            // 當調用一個空閒 handler 時,一個新 message 可以被分發,所以無需等待能夠直接查詢 pending message
            nextPollTimeoutMillis = 0;
        }
    }

nativePollOnce 是阻塞操做,其中 nextPollTimeoutMillis 表明下一個消息到來前,還須要等待的時長;當 nextPollTimeoutMillis = -1 時,表示消息隊列中無消息,會一直等待下去。

當處於空閒時,每每會執行 IdleHandler 中的方法。當 nativePollOnce() 返回後,next() 從 mMessages 中提取一個消息。

enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
        // 每個普通 Message 必須有一個 target
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            // 正在退出時,回收 msg,加入到消息池
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // p 爲 null (表明MessageQueue沒有消息) 或者 msg 的觸發時間是隊列中最先的,則進入該該分支
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;   // 當阻塞時須要喚醒
            } else {
                // 將消息按時間順序插入到 MessageQueue。通常地,不須要喚醒事件隊列,除非
                // 消息隊頭存在 barrier,而且同時 Message 是隊列中最先的異步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // 消息沒有退出,咱們認爲此時 mPtr != 0
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQueue 是按照 Message 觸發時間的前後順序排列的,隊頭的消息是將要最先觸發的消息。當有消息須要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應該插入的合適位置,以保證全部消息的時間順序。

removeMessages()

void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // 從消息隊列的頭部開始,移除全部符合條件的消息
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // 移除剩餘的符合要求的消息
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

這個移除消息的方法,採用了兩個 while 循環,第一個循環是從隊頭開始,移除符合條件的消息,第二個循環是從頭部移除完連續的知足條件的消息以後,再從隊列後面繼續查詢是否有知足條件的消息須要被移除。

總結

最後用一張圖,來表示整個消息機制:
handler_java.jpg

圖解:

        ✨ Handler經過sendMessage()發送Message到MessageQueue隊列;
        ✨ Looper經過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
        ✨ 通過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
        ✨ 將Message加入MessageQueue時,處往管道寫入字符,能夠會喚醒loop線程;若是MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler接口中的方法,每每用於作一些清理性地工做。

參考Blog

  01. https://blog.csdn.net/iisprin...
  02. http://gityuan.com/2015/12/26...

相關文章
相關標籤/搜索