Android 覆盤——幫你完全瞭解消息機制

1. 什麼是消息機制

說到消息機制,做爲一名 Android 開發者必定先想到的是 Handler。Handler 就是 Android 消息機制的上層接口,咱們可用經過 Handler 輕鬆的在不一樣的線程中切換任務,但 Handler 的實現還有兩個很重要的概念 MessageQueueLooperjava

MessageQueue 的翻譯是消息隊列,它的內部採用了單鏈表的結構存儲 Handler 對象發送的消息。android

Looper 的做用是不斷地查詢 MessageQueue 中是否有消息,若是 Looper 發現 MessageQueue 中存入了新的消息,它就會去處理這條消息,若是沒有新消息,Looper 就會以無限循環的方式去查詢 MessageQueue 中是否有新消息。數組

2. 爲何要有 Handler

2.1)官方文檔中 Handler 的主要做用

(1)安排未來某個時間點執行的 MessageRunnables
(2)在不一樣於當前的線程上執行的操做;安全

2.2)Handler 被用來作的最多的一件事就是更新主線程的 UI。

在 Android 開發中,默認子線程是不能夠更新 UI 的,這一點能夠從 View 的最高層級 ViewRootImpl 類中找到答案app

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}

ViewRootImpl 類中的 checkThread 方法會在更新 UI 前被執行,若是當前線程不是主線程,就會拋出 Only the original thread that created a view hierarchy can touch its views. 的異常異步

2.3)那麼 Android 爲何要設計爲只能在主線程中更新 UI 呢?

  • Android 在子線程中更新 UI 是不安全的,若是多個子線程同時修改一個控件的數據,後果是不可控的
  • 若是給 UI 更新機制加鎖,會下降 UI 的訪問效率,而且可能阻塞某些線程的執行

3. Handler 的用法

3.1)在主線程中建立 Handler

一般,咱們在主線程中建立 Handler 的寫法以下:ide

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

但這樣寫,系統會這樣提示:oop

This Handler class should be static or leaks might occur (anonymous android.os.Handler)
這個Handler類應該是靜態的,不然可能會發生泄漏

出現這個警告但緣由是,Handler 在 Activity 中做爲一個匿名內部類來定義,它的內部持有來 Activity 的實例。當 Activity 被用戶關閉時,由於 Handler 持有了 Activity 的引用,就形成了 Activity 沒法被回收,從而致使了內存泄漏。post

所以,在這裏推薦一種更加安全的寫法:學習

private static class MyHandler extends Handler{
    private WeakReference<Activity> weakReference;
    public MyHandler(Activity activity){
        weakReference = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
                case 0:                     
                  Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
                  break;
            }
    }
}

private MyHandler handler = new MyHandler(this);

經過靜態內部類的方式實現一個 Handler,此時內部類並不持有外部類對象的應用,須要在內部類的構造方法內增長一個外部類(Activity)的弱應用。這樣,即便 Activity 被關閉,Activity 也能順利被回收。

onCreate() 中的代碼以下:

btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
    }
});

這時候點擊按鈕的運行效果以下:
運行效果

3.2)在子線程中建立 Handler

在官方文檔中 Handler 的主要做用是在不一樣於當前線程的線程中執行操做,那麼如何用 Handler 解決兩個子線程之間的通訊呢?

請看代碼:

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                handler = new MyHandler(MainActivity.this);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Looper.loop();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
     }
});

此時點擊按鈕:
運行效果

可見當前的處理線程已經變成了子線程。

4. Handler 工做原理

若是細心的觀察代碼,能夠看到在子線程中建立 Handler 的時候調用了 Looper.prepare()Looper.loop() 兩個方法。這兩句代碼有什麼用呢?

咱們暫時能夠把 Looper 理解爲消息的管理者,它負責從 MessageQueue 中提取出消息,傳遞給 Handler 進行處理,每個 Handler 都必需要有一個 Looper,在 Handler 建立的時候,它會自動使用當前線程的 Looper,而 Looper.prepare() 的做用就是爲當前線程準備一個 Looper,Looper.loop() 的做用是開始查找當前 MessageQueue 中是否有了新的消息。

這就是 Handler 工做的第一步 :

4.1)採用當前線程的 Looper 建立 Handler

由於這裏主要講 Handler 的工做流程,建立 Looper 的具體過程放到文章的下面講解。咱們只要知道
Looper.prepare() 爲當前的線程建立了一個 Looper 對象便可。

可是,在主線程中建立 Handler 的時候,咱們並無看到 Looper.prepare() 的執行,這是由於在 UI 線程,即 ActivityThread 的建立過程當中,Looper 已經被建立好了。

咱們能夠在 ActivityThread 的 main() 方法中看到這樣一句代碼:

Looper.prepareMainLooper();

這個方法內部也調用了 Looper.prepare() 爲 UI 線程建立了一個 Looper。

4.2)經過 Handler 的 sendMessageAtTime() 方法發送 Message

爲何是 sendMessageAtTime?不是還有 sendMessage()sendEmptyMessage()sendEmptyMessageDelayed()sendEmptyMessageAtTime()sendMessageDelayed() 這麼多方法嗎?

經過閱讀這些方法的源碼能夠發現,這些方法最終調用的都是 sendMessageAtTime()

其次還有 post()postAtTime()postDelayed() 方法最終調用的也都是 sendMessageAtTime() 方法,只是多了一步調用 getPostMessage(Runnable r, Object token) 將 Runnable 封裝爲一個 Message 對象的 callback 裏。

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

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

那麼 sendMessageAtTime() 裏的具體操做是什麼呢?咱們去源碼裏一探究竟

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 先獲取當前 Handler 中的 MessageQueue,mQueue 在 Looper 的構造方法中進行初始化。
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // queue 不爲空,則執行 Handler.java 裏的另外一個 enqueueMessage() 方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

    
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 指定 msg 的 Target 對象爲當前的 Handler
    msg.target = this;
    if (mAsynchronous) {
       msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler 中的 enqueueMessage() ,最終會調用 MessageQueue.java 中的 enqueueMessage() 方法。

以後,Message 對象最終傳遞到 MessageQueue 即消息隊列裏中,在消息隊列裏的具體處理邏輯在文章的 MessageQueue 工做原理 部分會具體解釋。

4.3)Looper 處理消息後調用 Handler 的 dispatchMessage() 方法

在第二步將消息插入消息隊列後,Looper 就開始遍歷消息隊列,找到新的消息,再通知 Handler 去執行這條消息,調用的就是 Handler 的 dispatchMessage() 方法。

public void dispatchMessage(Message msg) {
   // msg 的 callback 對象就是一個 Runnable
   if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 檢查 mCallback 是否爲空,不爲空就執行它內部定義的 handleMessage() 方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 若是 mCallback 爲空,就執行在實例化 Handler 過程當中咱們本身定義的 handleMessage() 方法中的內容
        handleMessage(msg);
    }
}

dispatchMessage() 方法首先會檢查 Message 的 Callback 對象是否爲空,callback 就是經過 post() 方法傳遞的 Runnable 對象,若是 callback 不爲空,就去執行 handleCallback() 方法。

handleCallback() 方法的實現也很簡單,它在內部執行了 Runnable 的 run() 方法

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

若是 callback 對象爲空,就檢查 mCallback 是否爲空,不爲空就執行它的定義的 handleMessage() 方法,若沒有 mCallback,最終將直接執行咱們在繼承 Handler 時本身定義的 handleMessage() 方法中的代碼。

Callback 是 Handler 中定義的的一個接口,它的代碼以下:

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 */
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 接口的話,咱們能夠直接實例化一個 Handler 而不用去實現一個 Handler 的子類,

private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});

5. MessageQueue 工做原理

咱們從上一部分的 MessageQueue.java 中的 enqueueMessage() 方法開始入手。

5.1)enqueueMessage()

代碼量有點多,要耐心看哦!

boolean enqueueMessage(Message msg, long when) {
    // 檢查當前 msg 的 target 是否爲空
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    // msg 若是正在被執行,就拋出異常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        // 在 quit() 方法中,mQuitting 會被設置爲 true
        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 正在執行
        msg.markInUse();
        // 設置 msg 的 when 爲傳進來的 when 參數,when 是 Message 想要被執行的時間
        msg.when = when;
        // 獲得當前消息隊列的頭部消息
        Message p = mMessages;
        boolean needWake;
        // 當前消息隊列爲空,新消息的觸發時間爲 0,或者新消息的觸發時間早於消息中第一條消息的觸發時間
        // 則將新消息插入到隊列的頭部,做爲當前消息隊列的第一條消息
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 將當前消息的下一條消息指向頭部消息
            msg.next = p;
            // 頭部消息修改成當前消息
            mMessages = msg;
            // 當阻塞時,須要喚醒
            needWake = mBlocked;
        } else {
            // 將新消息插入到當前消息隊列當中,(不是頭部)
            // 一般咱們沒必要喚醒事件隊列,
            // 除非隊列頭部有消息障礙,而且消息是隊列中最先的異步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 開始循環便利消息隊列,比較新消息和隊列中消息的 when(觸發事件)的值,將新消息插入到適當位置
            for (;;) {
                // 循環第一次遍歷時,將當前隊列中的頭部消息賦值給 prev
                prev = p;
                // p 指向隊列中的第二個消息
                p = p.next;
                // 若是下一個消息爲空,或者新消息的觸發時間早於下一個消息,找到了要插入的位置,退出循環
                if (p == null || when < p.when) {
                    break;
                }
                // needWake 爲 true,而且 下一條消息是異步的,則不須要喚醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 將新消息插入到 p 以前,頭消息以後。
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // 若是須要喚醒,調用 nativeWake 方法去喚醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

執行完 enqueueMassage 方法,咱們新發送的 Message 就成功的插入了消息隊列當中。
可是除了插入新消息,咱們還須要從消息隊列中讀取消息,這又要怎麼作呢?

5.2)next()

Message next() {
    // 若是消息循環已退出,而且被丟棄,則返回空。
    // 這個將在應用重啓一個 looper 時發生
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    // 記錄空閒時處理的 IdlerHandler 數量,只在第一次迭代時爲 -1
    // IdleHandler 只在隊列爲空 或者 是頭部消息時執行
    int pendingIdleHandlerCount = -1;
    //  native 層使用的變量,設置的阻塞超時時長,0 爲不阻塞,-1 爲阻塞
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);
  
        // 嘗試檢索下一條消息。 若是找到則返回。
        synchronized (this) {
            // 獲取系統從開機到如今到時間
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            // 將隊列中到頭部消息賦值給 msg
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // msg 不爲空,可是這個 msg 沒有 handler,則這個 msg 爲柵欄
                // 開始遍歷,指到獲取第一個異步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                // 若是當前時間不到 msg 的觸發時間,則計算時間差,設置阻塞超時時長
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 當前時間到了 msg 的觸發時間,則獲取消息並返回
                    mBlocked = false;
                    // 若是當前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        // 當前 msg 爲頭部消息,則將下一個 msg 設置爲頭部消息
                        mMessages = msg.next;
                    }
                    // msg 的下一個 Message 對象置空,表示從消息隊列中取出來了這條 msg
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 標記 msg 正在使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 若是沒有消息,則設置阻塞時長爲 -1,直到被喚醒
                nextPollTimeoutMillis = -1;
            }

            // 全部的消息都被處理後,判斷是否退出,並返回 null。
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            // 第一次循環時,消息隊列爲空,或 當前時間未到消息的觸發時間,獲取 IdleHandler 的數量
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
  
            // pendingIdleHandlerCount 的數量爲 0 時,線程會繼續堵塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            // 判斷當前空閒時處理任務的handler是不是爲空,若是爲空,就實例化出新的對象
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 運行 IdleHandler,只有第一次循環時纔會運行
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            // 釋放 IdleHandler 的引用
            mPendingIdleHandlers[i] = null;

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

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

        // 重置 IdleHandler 的數量爲 0,確保不會重複運行它們
        pendingIdleHandlerCount = 0;

        // 在執行 IdleHandler 時,一個新的消息可能插入或消息隊列中的消息到了觸發時間
        // 因此將 nextPollTimeoutMillis 設爲 0,表示不須要阻塞,從新檢查消息隊列。
        nextPollTimeoutMillis = 0;
    }
}

至此,MessageQueue 的兩個最重要的方法已經分析完了,下面來看 Looper 如何循環地從消息隊列中取出消息。

6. Looper 工做原理

在講 Looper 以前,須要先理解 ThreadLocal 的工做原理

6.1)ThreadLocal 的工做原理

ThreadLocal 是一個線程內存儲數據的類,當不一樣的線程去訪問同一個 ThreadLocal 對象時,得到的值都是不同的,下面用一段代碼來證實

private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                mThreadLocal.set("Thread_A");
                Log.d("ThreadLocalValue",mThreadLocal.get());
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                mThreadLocal.set("Thread_B");
                Log.d("ThreadLocalValue",mThreadLocal.get());
            }
        }.start();        
    }
);

我在兩個線程中分別存入在 mThreadLocal 中存入了不一樣的值,而後在控制檯輸出它們的內容

不一樣線程訪問 ThreadLocal 對象

可見不一樣線程訪問同一個 ThreadLocal 對象獲得的值也是不同的。

ThreadLocal 實現這種特性的緣由也很簡單,下面來看它內部的 set 方法:

public void set(T value) {
    // 獲取當前線程 t
    Thread t = Thread.currentThread();
    // 根據當前線程 t,獲取當前線程的 ThreadLocalMap 對象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // map 不爲空,調用 ThreadLocalMap 的 set() 方法。
        map.set(this, value);
    else
        // map 爲空,則爲當前線程建立一個新的 ThreadLocalMap 對象
        createMap(t, value);
}

在 set 方法中,先獲取當前線程,而後獲取當前線程的 ThreadLocalMap 對象。getMap() 的 和 createMap() 的實現以下:

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

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

那麼 ThreadLocalMap 又是什麼呢,這裏是它的一部分源碼:

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    // 初始的 table 容量
    private static final int INITIAL_CAPACITY = 16;
  
    // Entry 數組用於存儲數據
    private Entry[] table;

    // table 的大小
    private int size = 0;

    // 負載因子,用於擴容
    private int threshold; // Default to 0

    // 設置負載因子爲固然容量大小的 2 / 3 
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }
  
    // 初始化 Entry 數組
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}

能夠將 ThreadLocalMap 看成一個哈希表,它的內部用 Entry 存儲相應的數據。

在 Thread 的屬性中有 ThreadLocal.ThreadLocalMap threadLocals = null;,因此每個線程內部,都持有一個 ThreadLocalMap 對象,系統才能夠經過 getMap() 方法獲取當前線程的 ThreadLocalMap 對象。

在 ThreadLocal 中調用 set 方法,實際上會調用 ThreadLocalMap 中的 set 方法,源碼以下:

// ThreadLocalMap 的 set 方法
private void set(ThreadLocal<?> key, Object value) {

    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.

    // 首先獲取當前 ThreadLocal 對象的 table 屬性,table 一個 Entry 的數組
    // Entry 至關於一個 HashMap,存儲了當前 ThreadLocal 對象和 Object 類型的 value 對象
    Entry[] tab = table;
    int len = tab.length;
    // 計算出存儲的位置
    int i = key.threadLocalHashCode & (len-1);

    // 遍歷 tab
    for (Entry e = tab[i];
        e != null;
        e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        // 若是 tab 中已經存在了相同的 key 值,就覆蓋它原有的 value
        if (k == key) {
            e.value = value;
            return;
        }
        // 若是 當前 entrt 的 key 爲 null,調用 replaceStaleEntry 方法清楚全部 key 爲 null 的數據
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
        // 都不知足,就新建一個 Entry 對象
    tab[i] = new Entry(key, value);
    int sz = ++size;
    // ThreadLocalMap 的容量到達閥值後擴容
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

ThreadLocal 中的 get() 方法和 set() 方法同樣,都是對 Thread 中對 ThreadLocalMap 進行操做

public T get() {
    // 獲取當前線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的 ThreadLocalMap 對象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 獲取 ThreadLocalMap 中對應當前線程的 Entry 對象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 將 Entry 對象中的 value 取出來
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

6.2)Looper 中的 prepare() 方法

那麼 ThreadLocal 和 Looper 有什麼關係呢?咱們知道每個線程都有本身的 Looper,Looper 的做用域就是當前的線程,Android 系統中便經過 ThreadLocal 對象來存儲不一樣線程中的 Looper。

Looper 中 prepare() 方法爲當前線程建立一個 Looper 對象,咱們看一下它的實現:

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 將 Looper 對象保存到當前線程的 ThreadLocalMap 當中
    sThreadLocal.set(new Looper(quitAllowed));
}

這裏再看一下 Looper 的構造方法

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

能夠看到在一個 Looper 中建立了一個 MessageQueue,這裏咱們就能夠搞清楚 Handler、Looper 和 MessageQueue 的對應關係了:

每一個線程都有一個 Looper 對象,在 Looper 對象的初始化過程當中,會爲當前線程建立一個 MessageQueue,而一個線程中能夠有多個 Handler。

6.3)Looper 中的 loop() 方法:

prepare() 調用後,就是調用 loop() 方法:

/**
  * Run the message queue in this thread. Be sure to call
  * {@link #quit()} to end the loop.
  */
public static void loop() {
    // 經過 Thread Local 獲取當前線程的 Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 獲取當前 Looper 對象的 MessageQueue
    final MessageQueue queue = me.mQueue;

    // 清空遠程調用端進程的身份,確保此線程的身份是本地進程的身份,並跟蹤該身份令牌
    // 這裏主要用於保證消息處理是發生在當前 Looper 所在的線程
    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;
        }
        
        // 用 logging 打印日誌,默認爲 null,可經過 setMessageLogging() 方法來指定
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        
        // 開始跟蹤,並寫入跟蹤消息,用於 debug 功能
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }        
        ...
        ...
        try {
            // // 經過 Handler 分發消息
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                // 中止跟蹤
                Trace.traceEnd(traceTag);
            }
        }
        
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        //  確保在分發消息的過程當中線程的身份沒有改變
        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.recycleUnchecked();
    }
}

能夠看到 loop() 方法就是不停的遍歷消息隊列中的消息,當發現有新的消息時,便調用 Handler 的 dispatchMessage() 方法。

6.4)getMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
  /**
    * Returns the application's main looper, which lives in the main thread of the application.
    */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

getMainLooper() 方法用於返回當前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的創建時經過調用
prepareMainLooper() 方法建立。

6.5)quit() 和 quitSafely()

在子線程中,若是手動爲其建立了Looper,那麼在全部消息處理完成以後應該調用 quit() 方法終止消息循環,否則 Looper 就會一直處於等待狀態。

public void quitSafely() {
    mQueue.quit(true);
}

public void quit() {
    mQueue.quit(false);
}

能夠看到這兩個方法都調用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的參數爲 true,quit 的參數爲 false。

void quit(boolean safe) {
    // 主線程不退出消息循環
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        // 若是已經退出了,直接 return
        if (mQuitting) {
            return;
        }

        // 標記爲已經退出
        mQuitting = true;
        // 若是 safe 的值爲 true,執行完當前的消息後退出消息循環
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            // 直接退出消息循環
            removeAllMessagesLocked();
        }
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

quitSafely() 會等待當前消息執行完畢後退出消息循環,而 quit() 方法會直接退出消息循環。

private void removeAllMessagesLocked() {
    // 獲取當前 MessageQueue 的頭部消息
    Message p = mMessages;
    while (p != null) {
        // 循環遍歷全部的 Message
        Message n = p.next;
        // 回收消息,並把消息放入消息池
        p.recycleUnchecked();
        p = n;
    }
    // 將頭部消息置爲空
    mMessages = null;
}

private void removeAllFutureMessagesLocked() {
    // 獲取系統從開機到如今到時間
    final long now = SystemClock.uptimeMillis();
    // 將當前的頭部消息賦值給 p
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            // 若是當前頭部消息將要執行的時間大於系統開機到如今的時間,則執行 removeAllMessagesLocked() 方法
            // 清空 MessageQueue 隊列
            removeAllMessagesLocked();
        } else {
            Message n;
            // 遍歷當前的 MessageQueue,直到某個消息的執行時間小於 now 值(即這個消息正在執行)
            // 將這個消息的 next 賦值爲 null
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            // 回收不會被執行的 Message
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

終於講完了,但願你們能經過個人文章,完全理解 Handler 的機制,但個人能力有限,若是存在錯誤的地方,還請指出。

零碎的東西不少,爲了方便你們記憶,我把上面的內容作成了思惟導圖,須要的朋友能夠保存下來,偶爾看一下,幫助本身記憶。

Android 消息機制

歡迎關注本文做者:

掃碼關注並回復「乾貨」,獲取我整理的千G Android、iOS、JavaWeb、大數據、人工智能等學習資源。

相關文章
相關標籤/搜索