Android中的消息機制——Looper、Handler、MessageQueue與Message

本篇提綱.png

一.Looper、Handler、MessageQueue與Message的關(guān)系與相關(guān)概念

1.什么是Android消息處理機制?

“消息”是windows運行機制中一個基本而又重要的概念。消息是一個報告事件發(fā)生的通知,消息驅(qū)動是圍繞消息的產(chǎn)生與處理展開的,并依靠消息循環(huán)機制來實現(xiàn)(百度百科)。與Windows系統(tǒng)一樣,Android也是消息驅(qū)動型的系統(tǒng)。引用一下消息驅(qū)動機制的四要素:
??①接收消息的“消息隊列”
??②阻塞式地從消息隊列中接收消息并進行處理的“線程”
??③可發(fā)送的“消息的格式”
??④“消息發(fā)送函數(shù)”
與之對應(yīng),Android系統(tǒng)中對應(yīng)實現(xiàn)了:
??①接收消息的“消息隊列” —— MessageQueue
??②阻塞式地從消息隊列中接收消息并進行處理的“線程” —— Thread+Looper
??③可發(fā)送的“消息的格式” —— Message
??④“消息發(fā)送函數(shù)”—— Handler的post()和sendMessage()
??Android有大量的消息驅(qū)動方式來進行交互,比如Android的四大組件——Activity, Service, Broadcast, ContentProvider的啟動過程的交互,都離不開消息機制。

2.Handler

Android的消息機制,很多時候我們也稱之為“Handler機制”,可見Handler這個東西是相當(dāng)重要了~~那么Handler是用來干什么的呢?剛剛筆者在刷知乎的時候看到有大神懟(教導(dǎo))一個Android菜鳥的時候其中就提到了一點“...以為Handler就是用來更新UI的...”——好吧,真是垂死病中驚坐起——開始學(xué)Android那會筆者也曾經(jīng)這么認為(捂臉)。
??如果你也這么認為,那么從今天起你就要放棄這種狹隘的想法——Handler是Android消息機制的上層接口,因此我們在開發(fā)中與Handler打交道的機會最多。Handler并不是專門用來更新UI的,只是開發(fā)者常常用它來更新UI。Handler的主要用于同一個進程間的線程通信,Handler用于更新UI的時候是"子線程與主線程通信";當(dāng)然,Handler也可以用于子線程之間通信。
??Handler的消息機制主要是就指“Handler的運行機制”,Handler的運行機制時需要底層的MessageQueue和Looper支持的。

3.MessageQueue

MessageQueue翻譯過來是"消息隊列"的意思,實際上它內(nèi)部的數(shù)據(jù)結(jié)構(gòu)不是隊列,而是單向鏈表;MessageQueue中儲存了大量的消息,由于一個線程同一時間只能處理一條消息,所以我們建了一個鏈表,將我們需要處理的消息按順序儲存起來,然后一項一項的交給需要的線程處理,這就是MessageQueue存在的價值。
??這里筆者想到一個問題——為什么MessageQueue要用鏈表而不用數(shù)組來作為數(shù)據(jù)結(jié)構(gòu)呢?再經(jīng)過網(wǎng)上查找博客+源碼閱讀,筆者認為是這樣的——之前我們在一片文章中有講過,單鏈表更適合做增刪的操作,數(shù)組更適合做隨機訪問的操作。再MessageQueue中,我們不止要做隨機訪問(這里不是真的隨機訪問,消息的讀取是根據(jù)計算出來的時間順序來的,后文會講)跟多的我們做的是插入刪除操作,MessageQueue.enqueueMessage()就是插入消息(消息的插入需要根據(jù)時間發(fā)送的時間順序來確定,不存在頭插還是尾插),而插入消息;而MessageQueue.next()則是讀取消息,且讀取的同時也伴隨這刪除的過程。試想一下,一個消息隊列要循環(huán)起來,必然要頻繁的進行插入/讀取操作,假如采用數(shù)組的話,這兩個操作的平均時間復(fù)雜度都是O(N/2),而鏈表為O(1),顯然鏈表更合適。

4.Looper

Looper和MessageQueue的消息就像水泵和井(里邊裝的是水)的關(guān)系一樣,我們有了消息(水),但是為了把水從井中抽取出來(循環(huán)起來),我們得有一個水泵作為動力,這個動力就是Looper。
??如果我們在一個線程中調(diào)用Looper.prepare()...Looper.loop(),那么你的線程就成功升級為了一個Looper線程,這意味著你的線程有了一個消息泵(Looper)和一個消息隊列(MessageQueue),此時你就可以調(diào)用Handler來進行線程間的通信了。
??我們應(yīng)用的UI線程也就是主線程,在應(yīng)用啟動的時候,系統(tǒng)會自動初始化一個Looper,也就是說,我們的UI線程默認是Looper線程。這也就是為什么主線程中直接調(diào)用Handler沒什么事,但是再子線程中創(chuàng)建Handler需要哦手動調(diào)用Looper.prepare()...Looper.loop()的和原因。

5.Message

Message也就是消息,井中的水。一個Message包括了消息類型(what),消息內(nèi)容(arg1,arg2),發(fā)送它的Handler(target),Runnable回調(diào)接口等:

    public int what;        //數(shù)據(jù)類型
    public int arg1;        //簡單的整數(shù)值
    public int arg2;        //簡單的整數(shù)值可以直接發(fā)送,是一種替代setData(Bundle)的低成本方案,更加省資源
    public Object obj;
    ......
    /*package*/ int flags;
    /*package*/ long when;          //Handler發(fā)送一個消息之后,返回此消息的目標(biāo)交付時間(以毫秒為單位)。
    /*package*/ Bundle data;        //Bundle可以攜帶更復(fù)雜的數(shù)據(jù)類型
    /*package*/ Handler target;     //哪個Handler發(fā)送的消息
    /*package*/ Runnable callback;

    //可以看到,Message帶了一個指向一下個節(jié)點的鏈,也就是說,MessageQueue內(nèi)部維護的實際上是一個鏈表
    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;       //消息池
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;    //消息池的最大容量

講到這里,我們先上一張圖加深一下大家對于這幾個東西的直觀認識:

Handler循環(huán)機制.png

二.子線程與主線程Handler通信原理(子線程是如何通過Handler更新UI的)

1.一些熟悉的場景
  private Handler handler = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what) {      //判斷標(biāo)志位
               case 1:
                   //更新UI操作
                   break;
           }
        }
      };

       public class MyThread extends Thread {
         @Override
            public void run() {
              super.run();
              //耗時操作
            }
          Message msg =Message.obtain();//從全局池中返回一個message實例,避免多次創(chuàng)建(如new Message)
          msg.obj = data;
          msg.what=1;   //標(biāo)志消息的標(biāo)志
          handler.sendMessage(msg);
       }

       new MyThread().start();

上面代碼是我們再Android開發(fā)中經(jīng)常寫的一段代碼,其主要作用是再子線程中進行耗時操作,并通過Handler向主線程中發(fā)送消息,通知主線程做出相應(yīng)的UI變化。注意這段代碼中,Handler是再主線程中創(chuàng)建的,因此不需要手動調(diào)用Looper.prepare()添加Looper。
??如果我們試圖再子線程中創(chuàng)建一個Handler,如:

      Handler handler ;
      public class MyThread extends Thread {
         @Override
            public void run() {
              super.run();
              //耗時操作
            }

          handler = new Handler();
          Message msg =Message.obtain();//從全局池中返回一個message實例,避免多次創(chuàng)建(如new Message)
          msg.obj = data;
          msg.what=1;   //標(biāo)志消息的標(biāo)志
          handler.sendMessage(msg);
       }

       new MyThread().start();

那么很顯然,這個時候就會出bug了~~為了解決這個bug,我們需要手動再子線程中創(chuàng)建Looper:


      handler = new Handler();
      Looper.prepare();
      Message msg =Message.obtain();//從全局池中返回一個message實例,避免多次創(chuàng)建(如new Message)
      msg.obj = data;
      msg.what=1;   //標(biāo)志消息的標(biāo)志
      handler.sendMessage(msg);
      Looper.loop();

為什么是這樣呢?接下來從源碼的解讀分析

2.Looper

(frameworks/base/core/java/android/os/Looper)

(1)Looper.perpare()
public final class Looper {
    private static final String TAG = "Looper";

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

    final MessageQueue mQueue;
    final Thread mThread;

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

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

}

首先我們看到Looper.prepare()方法中調(diào)用了prepare(ture)方法,其中true表示,該Looper是可以被終止的。因為我們是在子線程中創(chuàng)建的Looper,當(dāng)子線程的消息處理完之后,理應(yīng)把改Looper終止掉(MessageQueue.quit)。但是在主線程的Looper中:

    public static void prepareMainLooper() {
        prepare(false);
        ......
    }

prepareMainLooper就是給主線程添加Looper,可以看到,主線程中的prepare(false)中的參數(shù)false表示的是,主線程中的Looper不能被終止掉,畢竟它是整個應(yīng)用的生命,需要時刻準備著處理或者正在處理應(yīng)用中的各種消息。
??好了我們接著看上面的prepare()中干了什么。這里出現(xiàn)了一個新的重量級的東西:sThreadLocal,它是ThreadLocal類的實例,關(guān)于ThreadLocal類是干什么的,我們在這里不做過多解釋,我們只需要知道他是用于儲存不同線程唯一對象的一個東西,即多個線程在ThreadLocal類中,通過ThreadLocal.set()保存了自己的變量之后,那么我們再各個子線程中調(diào)用ThreadLocal.get()方法,得到的仍然是當(dāng)前線程之前存進去的那個值。各個線程存取各自的值,不會產(chǎn)生沖突。
??知道了ThreadLocal的作用之后,我們在來看sThreadLocal.set(new Looper(quitAllowed));這句表示我們再ThreadLocal類中保存了一個Looper對象(new Looper()),根據(jù)上面對ThreadLocal類的介紹,如果我們再當(dāng)前線程中調(diào)用ThreadLocal.get()方法,則會得到本線程之前保存的唯一的變量。因此:

    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }

這個if表示的是,如果sThreadLocal.get() != null,說明當(dāng)前線程中已經(jīng)存在一個Looper,我們不能在一個線程中同時創(chuàng)建多個,所以此時會拋出異常。為了避免這種異常,我們可以在Looper.prepare()之前調(diào)用Looper.myLooper()類來返回當(dāng)前線程中的Looper對象,判斷為空之后,再調(diào)用prepare():

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Ok,比比了這么多,我們接著看new Looper()中Looper中的構(gòu)造函數(shù)中做了什么:

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

這里出現(xiàn)了消息隊列MessageQueue(quitAllowed),其中參數(shù)就是表示是否允許Looper退出的標(biāo)示符。可以看到,在Looper中我們創(chuàng)建了一個MessageQueue實例:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
```
,此時我們的Looper就擁有了MessageQueue的對象引用。

###### (2)Looper.Loop()
??在Looper.perpare()調(diào)用完即我們?yōu)榫€程準備好Looper之后,最后一步我們還需要調(diào)用Loop()讓整個Looper循環(huán)起來,這樣消息才能發(fā)送出去:
```
    public static void 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;
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            ......
            msg.target.dispatchMessage(msg);
            ......
            msg.recycleUnchecked();     //釋放占據(jù)的資源
        }
    }
```
首先調(diào)用了我們剛說過的myLooper()來獲取當(dāng)前線程再ThreadLcoal類中儲存的Looper,如果Looper為空則會拋出異常,提示當(dāng)前線程沒有Looper。然后`inal MessageQueue queue = me.mQueue;`是獲取當(dāng)前線程的Looper中的MessageQueue對象。
??之后這個Looper便進入了一個無限循環(huán)的狀態(tài)——for(;;),`Message msg = queue.next();`是一條一條遍歷整個消息隊列,拿出msg消息。
??而`msg.target.dispatchMessage(msg);`這句中的**msg.target**實際上就是當(dāng)前消息的目標(biāo)Handler,也就是**哪個線程中的Handler發(fā)送的消息,當(dāng)然,這個發(fā)送它的Handler也要在自己所在的線程中接受這條消息。**
??`msg.recycleUnchecked();`這句是將這天消息放入Message類中的消息池中。
從上面我們基本上可以得出——**Looper > MessageQueue > Message**的關(guān)系,也就是說,**每個Looper中維護了一個消息隊列,而一個消息隊列中則以鏈表的形式排列著一條條消息。**


##### 3.MessageQueue.next()
(frameworks/base/core/java/android/os/MessageQueue)
上面的Looper類中我們實例化了一個MessageQueue對象,并調(diào)用了MessageQueue.next()類方法:

```
    Message next() {
        final long ptr = mPtr;      //當(dāng)消息循環(huán)已經(jīng)退出,則直接返回
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration 循環(huán)迭代的首次為-1,也就是初始值為-1
        int nextPollTimeoutMillis = 0;
        for (;;) {      //消息隊列開始無限循環(huán)
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            //阻塞操作的方法,當(dāng)?shù)却齨extPollTimeoutMillis時長,或者消息隊列里邊有了消息被喚醒時,
            //只有滿足這兩個條件該方法才會返回,for循環(huán)才能往下執(zhí)行下面的代碼,否則就一直在這等著
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {   //這一段同步塊代碼中就是再檢索下一條message,如果找到了就返回
                final long now = SystemClock.uptimeMillis();    //手機開機到現(xiàn)在的時間(毫秒為單位),手機睡眠的時間不包括再內(nèi)
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {            //如果此時消息不為null,但是這個消息找不到發(fā)送它的Handler,說明為不合法消息,放棄并尋找下一條異步消息
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {  //如果消息不為null(這次是個正常消息)
                    if (now < msg.when) {
                        // 雖然消息是正常的,但是還沒到發(fā)送的時間。msg.when表示消息發(fā)送的時間,因為我們可能調(diào)用了postDelay()
                        //延遲發(fā)送。我們之前說過,當(dāng)?shù)却齨extPollTimeoutMillis時長后nativePollOnce()方法就會返回。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {    //else則表示,成功抓到了一條消息
                        mBlocked = false;
                        if (prevMsg != null) {      //單鏈表操作,下面講
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
                ......
            }
            ......
        }
    }
```
首先:
```
    final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
```
這個mPtr我們之前在MessageQueue的構(gòu)造方法中提到過:
```
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
```
它通過` mPtr = nativeInit();`得到值,`nativeInit()`是一個native層的方法,根據(jù)注釋來看,它主要是判斷Looper目前的狀態(tài)。`if (ptr == 0) {`表示的是當(dāng)前線程的Looper已經(jīng)退出或者被處理掉了,這種情況發(fā)生在“應(yīng)用在退出之后試圖重啟Looper”的情況下,這種情況是不被允許的,因此此時`return null;`
??上面這段中還要講的就是單鏈表的操作:
```
    if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        msg.markInUse();    //表示這個消息已經(jīng)被傳遞(使用了)
        return msg;
    }
```
首先,再synchronized (this)同步塊一開始的時候,有一個**全局變量mMessages,這實際上就是等待處理消息**,這個變量很重要,之后我們會多次遇到。這里還有一個叫prevMsg的Message,用于保存找不到msg.target的消息(廢消息)。OK,回到鏈表操作中來,`if (prevMsg != null)`,很遺憾,有一個不合法的消息,此時上面已經(jīng)經(jīng)歷了`prevMsg = msg; msg = msg.next;`這兩步,加上`prevMsg.next = msg.next;`這一步,實際上就是prevMsg的后繼引用跳過了msg,直接指向了msg的下一位(注意prevMsg是不合法的,但是它的下一位msg是合法的)。
??再看else,說明沒有不合法的消息,`Message msg = mMessages;`加上`mMessages = msg.next;`,這兩步實際上和上面一樣,也是工作指針后移,越過了msg。**也就是msg出隊,msg的下一條消息成為mMessages(待處理消息),否則就進入一個阻塞狀態(tài),一直等到有新的消息入隊。**
??OK,上面if和else中越過的msg,就是我們要返回的正常消息,接著兩句:`msg.next = null;`和`return msg;`就可以知道,前者是將msg的后繼引用清空(將它從鏈表中刪除),然后return。

??通過上面的分析我們知道,**MessageQueue.next()作用就是遍歷鏈表,找出一個合法的msg,將它從鏈表中刪除后返回**,這實際上也就是一個**消息出隊**的過程

##### 4.Handler
###### (1)Handler.post/sendMessage將一個消息添加到消息隊列中
??上面我們已經(jīng)講完了Handler通信的兩個重要的基礎(chǔ)類——**MessageQueue**和**Looper**,接下來我們分析一下Handler是如何將一個消息發(fā)送出去的。
??我們從Handler的構(gòu)造函數(shù)開始看起:
```
    public Handler() {
        this(null, false);
    }
    ......
    public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
```
可以看到,Hnadler在發(fā)送消息的時候先獲取當(dāng)前線程的Looper,然后做一次looper的非空判斷;接著獲取了Looper中的MessageQueue對象。這樣,我們的Handler已經(jīng)和Looer以及MessageQueue取得了聯(lián)系。
??接著回到文章最開始的時候舉的那個例子中,handler.sendMessage()方法:
```
    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");
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
```
上面幾個方法的邏輯都比較清晰,可以看到,最終調(diào)用了MessageQueue的enqueueMessage()方法,其中第一個參數(shù)為msg,第二個參數(shù)**uptimeMillis = SystemClock.uptimeMillis() + delayMillis**,也就是**開機到現(xiàn)在的時間(不包括睡眠時間)+我們設(shè)定的delay時間**,接下來我們看看enqueueMessage()方法:
```
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {   //目標(biāo)Handler為null
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {    //msg已經(jīng)用過了  
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {        //如果這個Looper正在退出,回收msg,加入到消息池
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                msg.recycle();      //釋放msg資源
                return false;
            }

            msg.markInUse();        //表示這個msg已經(jīng)被使用了
            //這里我們可以知道,Message類的when屬性實際上就是SystemClock.uptimeMillis() + delayMillis
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            //如果p == null,說明MessageQueue中沒有消息;或者msg的時間是這個消息隊列中最靠前的
                msg.next = p;       //將這個msg提取出來,并復(fù)制給mMessages,mMessages會在上面的
                mMessages = msg;    //MessageQueue.next()方法中進行一系列判斷后返回
                needWake = mBlocked;    //當(dāng)阻塞時需要喚醒
            } else {        //elss,說明此時我們發(fā)送過來的消息需要按照一定規(guī)則插入到隊列中
                //將消息按時間順序插入到MessageQueue。一般地,不需要喚醒事件隊列,除非
                //消息隊頭存在barrier,并且同時Message是隊列中最早的異步消息。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
      ①        for (;;) {
                    prev = p;
                    p = p.next;     //工作指針后移并且循環(huán)遍歷鏈表
                    if (p == null || when < p.when) {   //找到一個不為空并且發(fā)送時間大于當(dāng)前發(fā)送時間的消息,
                        break;      //跳出循環(huán),準備把msg(要發(fā)送的消息)查到這條消息之前
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
       ②       msg.next = p; // 單鏈表插入
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
```
??MessageQueue是按照**Message觸發(fā)時間的先后順序**排列的,隊頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊列時,會**從隊列頭開始遍歷**,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時間順序。
??上面代碼中①處的for()循環(huán)就是在循環(huán)遍歷MessageQueue以找到合適的msg插位置,②處的代碼實際上就是一個單鏈表插入的過程,我們可以把整個插入的代碼連起來,這樣更容易看出:
```
    prev = p;
    p = p.next;
    msg.next = p;
    prev.next = msg;
```
沒騙你吧~~這就是把我們的要發(fā)送的消息msg插入到了鏈表中的p節(jié)點之前。

??**我們需要明確的一點是,這里的Handler.post/sendMessage方法是和上面的MessageQueue.next()方法是對應(yīng)的,他是消息的入隊操作。**

###### (2)Looper.loop()中調(diào)用Handler.dispatchMessage()接受并處理消息
??好了,講到這里,我們可以看到,Handler.post或者Handler.sendMessage方法,最終是將他們要發(fā)送的消息添加到了消息隊列中(Handler.post實際上也是調(diào)用了Handler.sendMessageDaley())。
??那么接受消息在哪呢?好吧看標(biāo)題你也知道,Looper.loop()方法中我們實現(xiàn)了消息的重寫與接收。我們回過頭去看Looper類,在該類中MessageQueue.next()這個消息出隊的方法調(diào)用完之后,出現(xiàn)了`msg.target.dispatchMessage(msg);`這句代碼,我們說過這句代碼中的**msg.target**就是消息的目標(biāo)Handler,于是我們回到Handler中看下這個方法:
```
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
```
??可以看到最終我們調(diào)用了一個空方法,為什么呢,因為**消息的最終回調(diào)是由我們控制的**,我們在創(chuàng)建handler的時候會在Handler所在的線程中(如果是子線程更新主線程UI的話,就在主線程中)重寫handleMessage方法,然后根據(jù)msg.what進行消息處理(對照開始給出的Hanlder常見場景)。

我們梳理一下整個流程:
```
① handler = new Handler();  //創(chuàng)建Handler對象
② Looper.prepare();     //準備好Looper,初始化MessageQueue
③ Message msg =Message.obtain();    //從消息池中取出一個可用的Message實例
④ msg.obj = data;           //消息的數(shù)據(jù)
   msg.what=1;              //標(biāo)志消息的標(biāo)志
⑤ handler.sendMessage(msg);     //發(fā)送消息,將一個消息添加到消息隊列中去
⑥ Looper.loop();        //使用loop使消息隊列循環(huán)起來,并進行消息的出隊刪除操作
```
Looper.loop()中的消息出隊之后,將調(diào)用Handler的dispatchMessage,最終大我們在代碼中重寫的handleMessage用以自定處理:
```
    private Handler handler = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what) {      //判斷標(biāo)志位
               case 1:
                   //更新UI操作
                   break;
           }
        }
      };
```
###### (3)Handler.post() & View.post() & Activity.runOnUiThread()
**①Handler.post()**
??上面我們說了Handler.sendMessage()方法,并且在文章開頭的實例中展示了Handler.sendMessage()的使用方法,下面我們來說一下Handler.post(),該方法的具體使用還是略微的有一點不同的:
```
    public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

```
可以看到,Hander.post()方法還是調(diào)用了sendMessageDelayed方法,這跟之前的流程是一樣的。這里我們要說的是`sendMessageDelayed(getPostMessage(r), 0);`中的getPostMessage(r)方法:
```
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
```
在這個方法中將消息的callback字段的值指定為傳入的Runnable對象。這個callback字段之前我們有遇到過——在Handler的dispatchMessage()方法中原來有做一個檢查,如果Message的callback等于null才會去調(diào)用handleMessage()方法,否則就調(diào)用handleCallback()方法:
```
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
```
handleCallback(msg):
```
    private static void handleCallback(Message message) {
        message.callback.run();
    }
```
這里的run也就是一開始的時候傳入的run接口。也就是說,如果通過post(Runnable r)傳遞消息的話,我們直接就可以在post()方法中進行UI操作:
```
    handler = new Handler();  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                handler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        // 在這里進行UI操作  
                    }  
                });  
            }  
        }).start();  
```
寫法上簡潔了很多,但是本質(zhì)上都是一樣的。

**②View.post()**
(source/android-24/android/view/View):
```
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }
```
可以看到同樣調(diào)用了Handler的post方法

**Activity.runOnUiThread()**
```
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
```
如果當(dāng)前的線程不等于UI線程(主線程),就去調(diào)用Handler的post()方法,否則就直接調(diào)用Runnable對象的run()方法。


##### 5.Message.obtain
上面我們需要解釋的一個東西便是`Message msg =Message.obtain();`這句,Message的obtain()方法中維護了一個消息池,其最大容量**MAX_POOL_SIZE = 50**
```
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {    //消息池不為空時從消息池中直接拿消息
                Message m = sPool;
                sPool = m.next; //工作指針后移,準備取出sPool
                m.next = null;  //從sPool中取出一個Message對象,并消息鏈表斷開
                m.flags = 0;    // 清除in-use flag
                sPoolSize--;    //消息池的可用大小進行減1操作
                return m;
            }
        }
        return new Message();   // 當(dāng)消息池為空時,直接創(chuàng)建Message對象
    }

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }
```
??我們當(dāng)然可以直接new Message()這樣來創(chuàng)建消息,但是一般來講我們還是應(yīng)該調(diào)用Message.obtain()方法來返回一個消息實例,以避免Message對象的多次創(chuàng)建。

好了,到此為止,我們調(diào)用Handler發(fā)送消息更新UI的整個流程就說完了。

#### 三.Activity啟動過程中UI線程的MainLooper的創(chuàng)建
??Actvity的啟動流程我們之前在一片文章中有講過,真正啟動Activity的是ActiivtyThread類中的main()方法:
```
    public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();

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

        ......
        Looper.loop();
        ......
    }
```
可以看到,我們在啟動一個Activity之前,我們在ActivityThread.main()方法中,調(diào)用了**Looper.prepareMainLooper()**方法:
```
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
```
該方法同上面的Looper.prepare()方法一樣,只不過這里準備的是主線程中的Looper,因此**prepare(false);**其中的參數(shù)false表示的是,該線程的Looper不能退出。之后調(diào)用myLooper則是獲取主線程中的Looper對象,這些和上面都沒什么區(qū)別。

??我們回到ActivityThread.main()中,在準備完主線程的Looper之后,`ActivityThread thread = new ActivityThread();`創(chuàng)建一個ActivityThread實例。`thread.attach(false);`**參數(shù)false表示這不是系統(tǒng)進程,是給普通應(yīng)用程序使用的進程。**
我們接著`thread.attach(false);`來看:
```
    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            ......
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ......
```
注意到`mgr.attachApplication(mAppThread);`這句,其中mAppThread是ApplicationThread的對象,**mgr**為**IActivityManager**接口類,而真正實現(xiàn)IActivityManager接口的是在ActivityManagerService(AMS)類中,關(guān)于AMS類中的代碼我們這里就先不詳細解釋了,我們只需要知道,在它當(dāng)中經(jīng)過一系列的處理~~最終:
```
app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r),
        r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward());
}
```
又回調(diào)到了ActivityThread類中:
```
    //該方法在ApplicationThread(Binder線程)中調(diào)用
    @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
```
可以看到,這里將一系列應(yīng)用的信息封裝在ActivityClientRecord中之后,最終調(diào)用了`sendMessage(H.LAUNCH_ACTIVITY, r);`發(fā)送消息:
```
    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
```
```
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
```
上面的“H”實際上就是一個Handler類:`private class H extends Handler {`,而這里的mH則是H的子類。接著往下看:
```
    public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    ......
```
可以看到,這里handleMessage收到了我們發(fā)送的`LAUNCH_ACTIVITY`也就是啟動Activity的請求,實際上這里還有`PAUSE_ACTIVITY`,`RESUME_ACTIVITY`等一系列請求的處理。
之后`handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");`-->`performLaunchActivity`-->`mInstrumentation.callActivityOnCreate`一系列流程之后,我們的Activity就啟動了,這個我們在之前的文章中有講過,這里不再獒述。

??給大家展示這一段,主要是為了說明,在Activity的啟動過程中,在系統(tǒng)層面也是調(diào)用Handler機制來進行一系列事件的處理,從而推動系統(tǒng)的循環(huán)進行。

站在巨人的肩膀上摘蘋果:
《Android開發(fā)藝術(shù)探索》
[郭霖 Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解](http://blog.csdn.net/guolin_blog/article/details/9991569)
[Gityuan Android消息機制1-Handler(Java層)](http://gityuan.com/2015/12/26/handler-message-framework/)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容