Android的消息機制

Android的消息機制主要是指Handler的運行機制和它所附帶的MessageQueue和Looper的工作過程。

Handler、MessageQueue和Looper這三者其實是一個整體。但是我們要想清楚這個整體的工作流程就需要逐個擊破。

下圖羅列了這次分享說的重點

Android的消息機制.png

一、爲什麼提供這種機制

系統之所以提供這種機制主要是爲了解決在子線程不能訪問UI的矛盾。

那麼問題來了......

1.爲什麼子線程不能訪問ui呢?

因爲Android的ui控件不是線程安全的。如果在多線程的情況下併發訪問就會導致ui控件處於不可預期的狀態。

2.那麼在這種情況下爲什麼不對ui控件的訪問加上鎖機制呢?

首先加上鎖的機制會使訪問ui控件的邏輯變得複雜,其次因爲鎖機制會阻塞某些線程的進行,從而降低UI訪問的效率。基於這兩個原因最好的辦法就是使用單線程處理UI控件。反之對於開發者來說只需要利用Handler切換一下線程就可以了,也不是很麻煩

二、MessageQueue(消息隊列)

Android的消息隊列是MessageQueue,它主要有兩個功能:插入和讀取。

它的內部實現是並不如它名字那樣是個隊列,而是用一個單鏈表來維護消息列表,我們知道單鏈表的數據結構實現插入和刪除的效率高。

插入的操作很簡單,本質就是單鏈表的插入操作。

//MessageQueue的插入操作  -enqueueMessage方法
  boolean enqueueMessage(Message msg, long when) {

     ......
     synchronized (this) {
     ......

     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;
            }
     }
     ......

}

讀取的操作next方法,通過無限的循環來檢查有沒有新消息,當有新消息來的時候,next方法會返回這條消息並將其從單鏈表中移除。

//MessageQueue的讀取操作(伴隨着刪除操作)-next方法

  Message next() {
        ......
 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) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
        .....
          }

三、ThreadLocal的工作原理

ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據和讀取數據。

它的特點通俗點說就是,不同的線程訪問同一個對象,它們通過ThreadLocal獲得的值是不一樣的。

還可以這麼神奇?

首先它是一個泛型類

public class ThreadLocal<T> {}

其次通過set方法我們可以看到它使用了Map的數據結構,它的key就是線程,value就是該對象的值。

//獲取到當前線程的該變量的值,如果沒有則初始化一個。

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

set方法將線程對應的值進行存儲
之後,通過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();
    }
    

最後拋出一個問題

爲什麼通過ThreadLocal可以在不同的線程中維護一套數據的副本並且互不干擾?

因爲不同的線程訪問同一個ThreadLocal的get方法,ThreadLocal內部會從各自的線程中取出一個數組,然後再從數組中根據當前的ThreadLocal的索引去查找對應的value值,這樣不同的線程中取出的數組自然是不同的。簡單的說就是它們所對ThreadLocal的讀寫操作僅限於各自的線程內部。

四、Looper的工作原理

我們知道,Handler的工作需要Looper,沒有Looper的線程就會報錯。

那麼Looper是做什麼的呢?

具體來說,就是不停的從MessageQueue中查看是否有新的消息,如果有消息就會立刻處理,否則就一直阻塞在那裏等待新的消息。

如何爲線程創建Looper?

很簡單,通過Looper.prepare就可以爲線程創建一個Looper,這樣這個線程就有了屬於他的Looper。之後調用Looper.loop()就可以開啓消息循環。

Looper最重要的就是loop方法,它的工作原理也很簡單,首先它是一個死循環,會調用MessageQueue的next方法不斷的獲取新的消息並處理,沒有消息的時候就會阻塞在那裏。直到Looper調用quit或者quitSafely方法。也就是說我們最後是要在合適的時候退出Looper的。否則就會一直循環下去。

五、Handler的工作原理

Handler的工作主要包含消息的發送和接收。

1.發送消息主要通過post和send的一系列方法實現

mHandler.postDelayed(runnable,0);

發送消息的過程只是向MessageQueue中插入一條消息,它的next方法就會把這條消息返回給Looper,Looper收到消息後就開始處理了。最終消息由Looper交由Handler處理。由於Looper裏面使用了ThreadLocal<Looper>,所以它就能夠將消息切換到指定的線程中執行。

2.處理消息

Looper將消息交由Handler處理,就會調用Handler的disaptchMessage方法。這時候就進入了處理消息的階段。

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


首先,檢查Message的callback是否爲null,不爲null就通過handleCallback處理消息。這個callback是一個Runnable對象,也就是我們在post方法裏傳遞的Runnable參數。

其次檢查mCallBack是否爲null,不爲空就調用handleMessage方法。CallBack是一種當我們不想通過派生子類創建Handler的另外一種實現方式。

最後調用Handler的handlerMessage來處理消息

到這裏我們就聊完了Android的消息機制,它的作用就是很輕鬆的將某個任務或者說是消息切換到指定的線程(Handler所在的線程)中執行。本質上來說,它並不是專門用於更新UI,只是常常被用在更新UI上。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章