天天看點

Handler Looper Message 之間的那些事

本文參考http://blog.csdn.net/lmj623565791/article/details/43452969 hongyang大神的文章… 加入了一些自己的解讀…

對于Handler Looper Message 之前一直隻是知道理論,知其然不知是以然,看了hongyang大神的源碼分析,寫個總結帖.

一.概念..

Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。

異步消息處理線程啟動後會進入一個無限的循環體之中,每循環一次,從其内部的消息隊列中取出一個消息,然後回調相應的消息處理函數,執行完成一個消息後則繼續循環。若消息隊列為空,線程則會阻塞等待。

說了這一堆,那麼和Handler 、 Looper 、Message有啥關系?其實Looper負責的就是建立一個MessageQueue,然後進入一個無限循環體不斷從該MessageQueue中讀取消息,而消息的建立者就是一個或多個Handler 。

二.源碼分析

說道Handler也是面試常客了… 當然這也是Android中比較重要的一個部分。

就按他們出場的先後順序來分析吧(源碼基于API 19,建議邊看邊根據源碼對照)

Looper

最先是Looper… 為什麼不是Handler? 這個問題和為什麼主線程中我們沒有調用Looper.prepare()的回答其實是一樣的…

…App啟動時 已經調用Looper.prepare()方法

那麼我們來看看Looper.prepare()是做什麼的呢..

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");

   }

sThreadLocal.set(new Looper(quitAllowed));

}
           

如果目前線程當中已經有Looper了 就抛出異常(這也是為什麼一個線程隻能有一個Looper執行個體 ,隻有一個MessageQueue執行個體的原因) ;

如果沒有就初始化一個Looper對象放入目前線程中

說明一下:sThreadLocal是什麼..看源碼便知 是用來存放目前目前線程的Looper對象的

static final ThreadLocalsThreadLocal = new ThreadLocal();

再看看Looper的構造方法

private Looper(boolean quitAllowed) {

    mQueue = new MessageQueue(quitAllowed);

    mRun = true;

    mThread = Thread.currentThread();

}
           

構造方法中,最主要的就是建立了一個MessageQueue,這東西是幹嘛用的呢? 存放Handler發來的消息(接下來會證明),是以俗稱 消息隊列。

繼續看Loop()方法 這個就不貼全部源碼了 挑重要部分進行分析…(建議參照源碼進行閱讀)

final Looper me = myLooper();

    if (me == null) {

         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this            thread.");

}
           

final MessageQueue queue = me.mQueue;

拿到目前線程中的Looper執行個體(如果Looper執行個體為空就會抛出異常)再去拿Looper中的MessageQueue執行個體

for (;;) {

Message msg = queue.next();  

if (msg == null) {

    return;

}

msg.target.dispatchMessage(msg);

msg.recycle();
           

}

依然隻上了關鍵代碼…

一個無限循環 不斷從MessageQueue執行個體中擷取下一個Message,未擷取到便傳回;

擷取到Message執行個體則調用msg.target.dispatchMessage(msg); 把消息交給Message執行個體的target的dispatchMessage方法處理,看到這裡可能會有疑問了,msg的target是什麼? 檢視源碼可以發現就是Handler對象,下面分析Handler的時候會說道;最後釋放msg

小結:Looper的主要作用

1.與目前線程綁定,并保證一個線程中隻有一個Looper執行個體,進而保證了一個線程中隻有一個MessageQueue執行個體。

2.loop()方法,就是不斷的從MessageQueue中取出消息,并交給msg.target.dispatchMessage(msg)去處理(交給Handler去處理,接下來會細說)

Handler

我們用Handler之前都會初始化一個Handler對來更新UI線程…等等. 上源碼. 來看看Handler是如何與MessageQueue關聯上的 ,還有在非UI線程中是如何将消息發送到MessageQueue當中去的

來看看Handler的構造方法

public Handler(Callback callback, boolean async) {

    // 省略相關檢查代碼

    mLooper = Looper.myLooper();     // Tag1

    if (mLooper == null) {

    throw new RuntimeException(

        "Can't create handler inside thread that has not called Looper.prepare()");

    }

    mQueue = mLooper.mQueue;      //Tag2

    mCallback = callback;

    mAsynchronous = async;

}
           

Tag1 調用了Looper.myLooper() 前面Looper分析環節說到了 這是從目前線程中拿出Looper執行個體

Tag2 将Looper執行個體中的MessageQueue執行個體取出來

此時,Handler和MessageQueue就關聯上了

辣麼? 我們我們在非UI線程中,是如何将消息傳遞給MessageQueue的呢… 繼續看源碼

平時通過Handler發送一條Message是不是通過

public final boolean sendMessage(Message msg){

    return sendMessageDelayed(msg, 0);

}
public final boolean sendMessageDelayed(Message msg, long delayMillis){

    if (delayMillis < 0) {

        delayMillis = 0;

    }

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

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

}
           

看到這裡,大家想起了什麼? 沒錯 mQueue就是Handler初始化的時候 從目前線程取出的Looper中取出來的MessageQueue

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; ?? msg.target指派為this,(大家還記得在前面Looper.loop()方法中不斷從消息隊列中讀消息 然後調用 msg.target.dispatchMessage(msg)來處理資訊 ) ;也就是将目前Handler執行個體作為msg的target屬性,最終調用了queue.enqueueMessage(msg, uptimeMillis)方法 将消息存入MessageQueue中去 ; 換一句話說 Handler發出來的消息 最終儲存到了MessageQueue中去

現在消息已經存入了MessageQueue中,前面提到Looper.loop() 會不斷的讀取MessageQueue中有無資訊,有資訊會通過msg.target(也就是Handler) 來調用dispatchMessage(msg) ,來看看源碼

/**

* 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);         

     }

}
           

最後調用了handleMessage(msg); 看到這裡是不是感覺很熟悉了 我們再快看看handleMessage(msg)的源碼

public void handleMessage(Message msg) {

}
           

裡面什麼都沒有? 什麼情況? 回想一下,平時我們初始化一個Handler執行個體 是不是都會複寫handleMessage方法? 因為最終回調是由我們來控制。說道這裡 整個異步消息處理的流程已經梳理完了 讓我們來總結一下

1、首先Looper.prepare()在本線程中儲存一個Looper執行個體,然後該執行個體中儲存一個MessageQueue對象;因為Looper.prepare()在一個線程中隻能調用一次,是以MessageQueue在一個線程中隻會存在一個。

2、Looper.loop()會讓目前線程進入一個無限循環,不端從MessageQueue的執行個體中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。

3、Handler的構造方法,會首先得到目前線程中儲存的Looper執行個體,進而與Looper執行個體中的MessageQueue想關聯。

4、Handler的sendMessage方法,會給msg的target指派為handler自身,然後加入MessageQueue中。

5、在構造Handler執行個體時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。

内容參考Hongyang大神的部落格… 概念和總結屬于搬運工… 源碼分析屬于二次加工,加上了一些自己的了解… 今天正好代碼重構完成… 忙裡偷閑寫了這篇總結帖… 希望對大家在學習Android的道路上 有所幫助…

繼續閱讀