Handler 工作原理和源碼解析

        在Android 開發中 有這樣一個最基本的場景,當我們通過網絡請求拿到服務端返給我們的數據後,我們需要把數據放到我們的頁面上,一般網絡請求的耗時操作我們都會在子線程中完成,如果這個時候你直接 去更新界面 就會報錯,告訴你當前線程不是主線程無法更新界面操作,這個時候身邊的老程序猿就會告訴你用Handler。

       那麼什麼是Handler 呢 ,簡單的來講其實就是Android 用來進行線程間通信的 ,如上面的例子 把子線程的消息發送給主線程 讓主線程去完成相應的操作。具體的Handler 是怎麼使用的這裏不做描述,代碼很簡單網上例子也很多。我們主要講講原理。

      我們在使用Handler的時候一般使用的就兩個方法,sendMessage()發送消息 和handleMessage() 分發消息並做相應的處理。我們可以進入Handler內部看看源碼。看源碼的時候我們首先看Handler的屬性,


    final MessageQueue mQueue;
    final Looper mLooper;

我們拿出主要的兩個屬性,MessageQueue  消息隊列,也就是說Handler 處理消息的方式是使用了消息隊列,瞭解隊列這個數據結構的就知道他的特性就是 FIFO (First Input First Output) 先進先出。也就是說 handler.sendMessage 的時候 就是把message 入隊列。看看Handler源碼中 發送消息的方法 是不是這樣的邏輯。

我整理了一下 Handler 中涉及發送消息的方法

從上面的圖中我們看到所有調用的發送消息的方法 (包括send , post )方法最終調用的是 enqueueMessage 方法

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

而在enqueueMessage 方法中調用的 MessageQueue 消息隊列的 enqueueMessage 方法

  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;

關於消息 隊列 消息是如何入隊的涉及到數據結構的內容不在這篇文章中描述,簡單講就是 for 一個死循環通過時間 when 找到這個消息的位置然後 添加進去,其中還涉及到掛起和喚醒等操作。

所以handler.sendMessage  等一系列發送消息的方法 就是把message 添加到消息隊列中。

接下來我們看之前還有一個屬性Looper ,Looper 的作用我們先看 Handler 構造函數

  mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;

handler 構造函數最終執行的 代碼片段中 有這個,Looper.myLooper() 獲取Looper 對象,如果null 會報錯。意思是線程中如果Looper 沒有實例化 那麼Handler 就不能創建,也就是說要想創建Handler 必須先實例化Looper。而且 這段代碼中  mQueue = mLooper.mQueue ;     MessageQueue 對象,我們也是從 Looper  中 獲取的。接下來看看Looper 的源碼

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

Looper 屬性中 有一個 ThreadLocal <Looper> 這個很重要,ThreadLocal 有點像 HashMap  也是以key ,value 的形式存儲數據,在這裏 ThreadLocal 的key 值就是 當前Looper 所在的線程 , value  就是Looper 對象。也就是說 Looper  是和 線程 一一對應的,通過線程就可以得到這個線程的唯一的Looper 對象。

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

之前說 創建Handler 之前要先Looper .prepare()   從這裏也可以看出  一個線程只能創建一個 Looper  對象。不然會報錯。

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

在Looper 構造方法中 實例化了一個 消息隊列 和獲取 當前線程 從這裏我們可以得到一個 對應關係 Thread --->Looper---->MessageQueue 一個 線程只有一個Looper  控制着唯一的 MessageQueue 

那麼Looper 是怎麼控制這個MessageQueue 的呢

  public static void loop() {
        final Looper me = myLooper();
        
        final MessageQueue queue = me.mQueue;
       
        for (;;) {
            Message msg = queue.next(); // might block
           
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked();
        }
    }

上面是我刪掉了部分代碼的 Looper.loop()方法中的代碼 從代碼中我們看出 loop 就是拿到 消息隊列 ,然後不停的循環這個消息隊列 從消息隊列中拿消息。 queue.next()就是消息隊列的出隊列方法。從消息隊列中拿到消息後 調用 message 的 target 的 dispatchMessage 方法  。 這個target 是什麼看看 Message 源碼 

   /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;
    

target  就是Handler  , 那麼 是什麼時候 我們吧Handler 放到Message  中去的呢。

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

再看 Handler 的發送消息的時候 msg.target = this  把消息和 Handler 綁在一起。同時Handler. dispatchMessage 方法調用的就是handleMessage 方法。也就是 從消息的發送 到消息的處理 handler 一直伴隨着這個消息,這樣一來即使這個消息隊列中 有多個handler 送進來的消息 分發出去的時候也不會 搞亂。誰送進來的誰拿走。

所以Looper 的作用就是讓這個線程的消息隊列 轉動起來 。而我們之前創建Handler 的時候說要先調用 Looper .prepare 方法 實例化 同時 還要 Looper. loop  讓消息隊列 轉動起來。但是我們在 實際運用中在activity 中使用Handler 的時候並沒有用到Looper 是爲什麼呢。這就和我們的主線程 有關,我們的主線程 也就是 UI線程在哪裏,似乎沒見過(在Android 源碼中隱藏起來了)。

public static void main(String[] args) {
            Looper.prepareMainLooper();
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

這是ActivityThread 類中的main 函數 也是我們APP 程序的入口,每個程序都有main 函數,Android也不例外。在 main 函數中 我們看到了 Looper.prepareMainLooper  初始化 。同時 Looper.loop  轉動起來,也就是說在程序已啓動 我們的主線程就已經初始化了Looper 並且讓 消息隊列開始工作了,所以之後 我們在 activity 中使用handler 就不需要再做了。好了Handler 的原理就是這樣,有什麼疑問可以留言

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