Android 從源碼角度分析消息處理機制(Handler,Looper,Message)

Android 從源碼角度分析消息處理機制(Handler,Looper,Message)

前言

在Android中,修改UI的操作必須要放入到主線程中。而我們的網絡請求往往是長時操作,需要放入到子線程進行請求。可以通過Handler實現不同線程間的通信。對於如何實現的,網上有很多的教程或博文,也解釋的非常清楚,這裏不在多敘。

但在使用過程中,我們可能會有一些疑問:

  • 爲什麼可以通過Handler實現不同的線程間通信。

  • 通過sendXXX()方法,是怎麼在handlerMessage()中回調的

  • 爲什麼主線程不需要Looper.prepare()方法,而子線程需要Looper.prepare()Looper.loop()方法。

可能很多博客都會從Handler,Looper,MessageQueue他們之間的聯繫上,去解釋Android 中的消息處理機制,本篇博客意在從源碼的角度分析其通信原理。

源碼分析

在通常的編碼中,onCreate()方法是一個Activity的入口,我們往往在這裏做一些初始化操作。而對於一個程序來說,ActivityThreadmain()方法,便是一個應用的入口,所以我們先看一下main()方法的源碼




    public static void main(String[] args) {

        // 省略..
        // 方法一,構造Looper 對象。
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        // 省略...

        // 方法二,開始消息隊列的循環
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

main()方法中,存在兩個關鍵性的方法,分別是方法一和方法二。從英文命名上,我們可以大致猜出他們的意思,分別是準備主線程的Looper對象和開始循環消息隊列。

進入到Looper.prepareMainLooper()方法中,


   public static void prepareMainLooper() {
        // 構造Looper 方法
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 獲取當前的Looper()對象,進行賦值到sMainLooper
            sMainLooper = myLooper();
        }
    }

prepare(false)方法構造屬於當前線程的Looper對象,傳入參數暫且不管,稍後會看其源碼。

然後判斷sMainLooper對象是否爲null,如果不爲null就會拋出異常,異常的內容“主線程的Looper對象已經準備”,在這裏,通過字段的命名可以看出sMainLooperLooper對象的靜態字段,並且保存主線程的Looper對象。

那麼從prepareMainLooper()和判斷條件可以得出,構造主線程的Looper對象,保存對象到sMainLooper中。如果sMainLooper不爲null,表明重複調用,拋出異常。

那麼進入到prepare(false)方法中,

    private static void prepare(boolean quitAllowed) {
        // 判斷是否創建過Looper 對象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 創建Looper 對象,並保存
        sThreadLocal.set(new Looper(quitAllowed));
    }

先是通過sThreadLocal.get()方法獲取當前線程的Looper對象,如果不爲null,則拋出異常“一個線程只能創建一個Looper對象。”

如果不爲null,則創建Looper對象並保存。

那麼sThreadLoacl又是什麼呢,從命名上可以看出他是一個靜態字段,看一下聲明,

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal類型,該類是java.lang中。看一下他的get()set()方法,因爲我們主要是通過這兩個方法獲取對應的Looper對象。

    // get 方法
    public T get() {
        // 獲取當前所在的線程,很關鍵
        Thread currentThread = Thread.currentThread();

        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }


    public void set(T value) {
        // 獲取當前的線程
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

這兩個方法,看起來有點暈暈的,我們不深究其細節實現,明白其功能即可。

get()能夠根據當前線程返回對應的Looper對象

set()能夠根據當前線程保存對應的Looper對象。

繼續回到prepare()方法中,我們通過get()判斷,通過set()進行保存。其中通過new Looper(quitAllowed)方法,構造Looper對象,看一下他的實現

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

Looper的構造方法中,構造了一個MessageQueue()對象並保存,同時保存當前線程的對象。那麼關鍵詞出現了,MessageQueue,這時網上很多分析中出現的關鍵類,負責消息的隊列。由此可以看出,Looper對象中保存有MessageQueue的實例。

在這裏,我們需要注意的是mQueuemThread,從命名可以看出都是成員變量,即他和Looper的實例都是一一對應的。


public final class Looper {

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    private static Looper sMainLooper;  // guarded by Looper.class
    // 每一個looper 對應的消息隊列
    final MessageQueue mQueue;
    // looper 對應的線程
    final Thread mThread;
}

將字段的具體聲明貼出,以便理解。

那麼,到此,整個Looper.prepareMainLooper()方法就大致分析完了。

Looper.loop()

ActivityThread.main()中,最後調用了Looper.loop()方法,看一下此方法的實現。


public static void loop() {

        // 1.獲取Looper 對象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 2.獲取Looper對象對應的消息隊列
        final MessageQueue queue = me.mQueue;

        // 3.無線循環,遍歷消息隊列
        for (;;) {
            Message msg = queue.next(); // 4.循環消息隊列
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg);// 5.分發消息

            msg.recycleUnchecked(); // 6.回收資源
        }
    }

中間省略了一些代碼,自上往下分析:

1的地方首先獲取到當前線程的Looper對象。

2的時候獲取到Looper對象的MessageQueue實例,在創建Looper時,構造方法中創建了和他一一對應的MessageQueue對象,主要是作爲消息隊列。

然後無限for循環,遍歷消息隊列。

通過MessageQueuenext()方法獲取到Message對象,具體的5和6,我們需要看一下Handler的實現後進行理解。

Handler的實現

通過上面的分析,我們知道在主線程中構造了Looper對象並且開始輪詢。那麼另一個關鍵人物Handler,勢必要分析他們直接的聯繫。

看一下Handler的構造方法


 public Handler(Callback callback, boolean async) {

        // ...
        // 獲取當前線程的Looper對象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 保存Looper對象的消息隊列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

通常的使用,都是直接使用new Handler(),最終仍會調用此構造方法,默認爲null和false。

在這個方法中,handler保存了當前線程的looper的實例和消息隊列。

通常的使用中通過handler.sendMessage()向主線程發送消息。看一下實現


 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(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對象賦值到msg.target中,並且調用MessageQueueenqueueMessage()方法。

到此,對Handler的分析就差不多結束了。回過頭,在ActivityThread.main()中,Looper.loop()我們還有一點沒有分析,就是無限循環的處理,再看一下源碼


        // 3.無線循環,遍歷消息隊列
        for (;;) {
            Message msg = queue.next(); // 4.循環消息隊列
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg);// 5.分發消息

            msg.recycleUnchecked(); // 6.回收資源
        }

我們知道,msg.targer是我們構造的handler,那麼繞了一圈有回到了自身的handler上,調用handler.dispatchMessage(msg)方法,看一下其實現,

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

其中由幾個判斷,最終不通過的時候調用handlerMessage(msg)方法。看一下之前的判斷

msg.callback指定的是什麼,在什麼情況下不等於null呢,在使用handler的時候,還有一種使用方式及handler.post(Runnable r),看一下他的實現

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

這兩個方法比較清晰,從這個地方可以看出,msg.callback會調用我們的Runnable對象。

而對於mCallback對象,在構造方法中傳入的,因爲平常沒有用過,都是使用無參的構造函數,基本上都爲null。

那麼,通過以上的分析,整個流程就比較清晰了:

  • ActivityThread.main()中調用Looper.prepareMainLooper()Looper.loop()方法。
  • Looper.prepareMainLooper()方法創建Looper對象,同時Looper對象中會保存與其對應的MessageQueue對象。
  • Looper.loop()調用之後對MesageQueue對象進行輪詢。
  • 在創建Handler的時候,會保存當前線程的Looper對象和消息隊列MessageQueue
  • 通過調用handler.sendXXX()將消息推送到消息隊列。
  • 消息隊列中實際上調用的是handler.handlerMessage()方法,產生了聯繫。

子線程和主線程的使用區別

在實際使用中,子線程如果想要構造handler對象,必須調用Looper.prepare()Looper.loop()方法。而主線程不需要,爲什麼呢?

如果對於上面的源碼分析理解清楚了可以清楚的知道,因爲主線程中已經在ActivityThread.main()即應用打開的初期初始化過Looper對象並開始了消息隊列。

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