Android消息隊列模型

Google官方給Handler的解釋如下:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable)postAtTime(Runnable, long)postDelayed(Runnable, long),sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.


消息隊列模型:


消息系統模型一般包括以上七個部分:消息原型、消息隊列、消息發送、消息循環、消息獲取、消息派發、消息處理。對應關係如下:

1.  Message爲消息原型,包含消息描述和數據

2.  MessageQueue爲消息隊列

3.  Looper完成消息循環

4.  Handler駕馭整個消息系統模型,統領Message、MessageQueue和Looper

三 Handler工作原理分析

  要了解Handler工作原理,先看一下這個系統模型具體組成的層次結構框架是個什麼樣的。

 

      

Looper:

  實現Thread的消息循環和消息派發,缺省情況下Thread是沒有這個消息循環的既沒有Looper;

需要主動去創建,然後啓動Looper的消息循環loop;與外部的交互通過Handler進行;

MessageQueue:

  消息隊列,由Looper所持有,但是消息的添加是通過Handler進行;

  

  消息循環和消息隊列都是屬於Thread,而Handler本身並不具有Looper和MessageQueue;

但是消息系統的建立和交互,是Thread將Looper和MessageQueue交給某個Handler維護建立消息系統模型。

  所以消息系統模型的核心就是Looper。消息循環和消息隊列都是由Looper建立的,

而建立Handler的關鍵就是這個Looper。

  一個Thread同時可以對應多個Handler,一個Handler同時只能屬於一個Thread。Handler屬於哪個

Thread取決於Handler在那個Thread中建立。

  在一個Thread中Looper也是唯一的,一個Thread對應一個Looper,建立Handler的Looper來自哪個Thread,

Handler屬於哪個Thread。

  故建立Thread消息系統,就是將Thread的Looper交給Handler去打理,實現消息系統模型,完成消息的異步處理。

  

Handler與Thread及Looper的關係可以用下面圖來表示:

    

 

       Handler並不等於Thread,必須通過Thread的Looper及其MessageQueue,

用來實現Thread消息系統模型,依附於Thread上。

 

在線程建立Handler時:

  使Handler滿足消息系統需要的條件,將Thread中的Looper和MessageQueue交給Handler來負責維護。

在線程中建立Handler,需要做以下工作:

  l  獲取Thread中的Looper交給Handler的成員變量引用維護;

  l  通過Looper獲取MessageQueue交給Handler的成員變量引用維護。

 

  那麼消息系統模型建立完成之後,按照消息系統運行,

從Handler來看就是發送消息派發消息,與此線程消息系統的交互都由Handler完成。

消息發送和派發接口:

  l  post(runnable)消息,Runnable是消息回調,經過消息循環引發消息回調函數執行;

  l  sendMessage(Message)消息,經過消息循環派發消息處理函數中處理消息;

  l  dispatchMessage       派發消息,若是post或帶有回調函數則執行回調函數,否則執行

 消息處理函數Handler的handleMessage(通常派生類重寫)。

 

  以上就是Handler如何實現Thread消息系統模型的大致介紹。

下面將具體分析是如何實現消息系統模型運行的。

 

四 Handler實現流程分析

  我們知道Handler就是一個消息系統的外殼,屬於某個Thread幷包裝了Thread的Looper

及其MessageQueue;與外部進行交互(同一個線程內或者線程之間),接收派發和處理消息,

消息系統模型的核心是Looper。

  下面看看Handler是如何建立跑起來的,以msg消息爲例,runnable實質是一樣。

1 Handler的建立


 

  Handler唯一屬於某個Thread,在某個Thread中建立Handler時,需要獲取Thread的Looper

及其MessageQueue,建立Handler關鍵是Looper的來源。

 

  Handler提供了好幾個構造函數但其本質一致:

由外部傳入Looper:當前線程或其他線程    

  public Handler(Looper looper) {
        //初始化構建消息系統參數 mLooper
= looper; mQueue = looper.mQueue; mCallback = null
;   }

從當前線程獲取:由創建Handler的Thread決定

       

複製代碼
  public Handler() {
        //初始化構建消息系統參數 mLooper
= Looper.myLooper(); mQueue = mLooper.mQueue; mCallback = null;   }   public static Looper myLooper() { return sThreadLocal.get(); }
複製代碼

 

  不管哪種方式,我們知道Thread在默認情況下是沒有建立消息循環Looper實例的。

要實現消息循環必須確保Thread的Looper建立。如何確保呢?

  Looper提供了靜態函數:

複製代碼
public static void prepare() {
     if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
     }

     sThreadLocal.set(new Looper());
}

//存儲線程的局部變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
複製代碼

看到這裏剛開始讓我很是奇怪和迷惑:

  Looper一個獨立的類,又不屬於某個Thread,而這裏創建Looper的函數又是靜態的,

屬於整個Looper類;創建Looper之後交給靜態成員變量sThreadLocal保存,獲取

sThreadLocal.get(),那麼一個靜態變量屬於整個類,屬性更改始終有效。一次創建之後

sThreadLocal.get()永遠都不等於null!

  

  而Thread和Looper是唯一對應的,那這裏豈不是所有的Thread都是用同一個Looper,不可能!

所以肯定這個ThreadLocal是有玄機的。網上一查:

  ThreadLocal:

    維護線程的變量,爲每個使用該變量的線程實例提供獨立的變量副本,每個線程都能夠獨立使用該變量,

    而互不影響。(詳細可參考:http://blog.csdn.net/qjyong/article/details/2158097

  所以每一個線程調用Looper.prepare時,都會創建爲其唯一的Looper。

 

  要建立Handler,需要先創建線程的Looper,才能建立消息系統模型。通過Looper我們建立了

Thread上的消息系統模型Handler,可以來進行消息系統的一系列流程了。

 

2 消息發送


 

消息發送兩種方式:post和sendMessage;

       post:針對runnable對象;Runnable是一個接口,就是一個回調函數(提供了run方法)

       sendMessage:針對Message對象;

       

       下面通過代碼具體看一下這個過程:

複製代碼
public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
複製代碼

 

  看到post和sendMessage發送消息時,僅僅是對象不同而已,Runnable和Message;

但實際上都是Message的形式來描述。

 

這跟我通常理解的消息機制不同:

  通常post消息是將消息加入到消息隊列中並不立即執行就返回,send消息是立即執行等待消息執行完才返回。

  而這裏post或者send都是將消息放入到消息隊列中,然後立即返回,等待消息循環時獲取消息被執行。

 

  這裏提供了衆多的消息發送方法來指定消息的執行時間和順序,具體可以查看源代碼。

消息執行順序是根據消息隊列中消息的排列順序而定。

  下面看一下發送消息後將消息加入到消息隊列中的代碼:

由Handler調用MessageQueue的enqueueMessage方法:

       

複製代碼
  final boolean enqueueMessage(Message msg, long when) {

              Message p = mMessages;

              if (p == null || when == 0 || when < p.when) {
                 msg.next = p;
                 mMessages = msg;
              }
              else {
Message prev
= null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; } ……   }
複製代碼

  

  可以看到是按照時間順序將消息加入到MessageQueue中;

現在將消息加入到消息隊列中存儲起來,消息並未得到處理,下一步必然是如何派發消息和處理消息。

 

3 消息派發


 

建立Thread消息循環由Looper完成,存在一個消息調度死循環:    

複製代碼
  public static void loop() {
       MessageQueue queue = me.mQueue;
       while (true) {
              Message msg = queue.next(); // might block
              if (msg != null) {
                     if (msg.target == null) {
                            // No target is a magic identifier for the quit message.
                            return;
                     }

                     //派發消息 到target(Handler)
            msg.target.dispatchMessage(msg);

            //回收Msg到msgPool
                     msg.recycle();
              }
       }
  }
複製代碼

  

  這裏看到消息派發是由Message的target完成,這個target是什麼呢?是一個Handler。

消息系統是通過Handler用來與外部交互,把消息派發出去。可以看到沒有這個Handler,消息循環將結束。

 

消息派發由Looper通過Handler完成:

複製代碼
  public void dispatchMessage(Message msg) {

       //首先判斷runnable對象
       if (msg.callback != null) {
              handleCallback(msg);
       }
       else {
              //整個消息系統的回調函數 可以不用實現自己Handler
              if (mCallback != null) {
                     if (mCallback.handleMessage(msg)) {
                            return;
                     }
              }
//消息處理 通常交給Handler派生類 handleMessage(msg); }   }
複製代碼

 

  通過消息派發,這樣就實現消息的異步處理。

 

4 消息原型


 

前面看到消息發送有兩種方式:

  post(Runnable對象),sendMessage(Message對象),而中間都是通過Message對象

保存在MessageQueue中。然後消息派發時處理方式不同。如果在sendMessage時將將消息對象

附上Runnable對象,則post和sendMessage沒有區別了。所以這兩種方式很好理解基本一致,處理的方式不同罷了。

  

  消息系統模型中,我們的真正的消息原型是什麼,都具有那些功能,下面看一下Message中到底

包含了那些東西,能有效幫助我們合理的運用消息系統來完成一些任務和處理。

Message消息原型:

複製代碼
  public final class Message implements Parcelable {
         //標識消息
         public int what;
         int flags;
         long when;
      
         //傳遞簡單數據
         public int arg1;
         public int arg2;
    
         //傳遞較複雜數據 對象
         public Object obj;
         Bundle data;

         //處理消息的目標Handler
         Handler target;   

         //消息派發時 執行的Runnable對象
         Runnable callback;  

         //使消息形成鏈表
         Message next;

         //建立一個消息pool,回收msg,以避免重複創建節約開銷
         private static Message sPool;
         private static int sPoolSize = 0;
         private static final int MAX_POOL_SIZE = 10;
  }  
複製代碼

  

  看到提供了很豐富的屬性來描述消息,針對具體問題選擇使用那些屬性去怎麼樣描述消息了。

  獲取新的Message對象時,Message提供了obtain方法:避免我們自己去分配Message新的對象,

通過obtain獲取,可能從MessagePool中獲取,節約開銷。

 

下面看一下這個MessagePool是如何建立的:

  通常消息處理完畢的時候,消息也基本上處於無用狀態可以釋放回收了。對於需要頻繁的創建釋放的對象來說,

創建和釋放類實例都是要開銷的,太頻繁的使開銷增大不好,像Message這種很有可能會頻繁的創建。

 

  於是我們可以將創建的對象用完之後保存在一個Pool裏面,以便再重複利用節約頻繁創建釋放開銷。

是如何建立的呢?必然是在消息處理完畢之後才能進行。

MessagePool建立:

複製代碼
public static void loop() {
       while (true) {
              //派發消息
              msg.target.dispatchMessage(msg);

              //消息處理完畢 回收
        msg.recycle();
    }
}     

public void recycle() {
       //回收Message 建立全局的MessagePool
       if (sPoolSize < MAX_POOL_SIZE) {
           next = sPool;
           sPool = this;
           sPoolSize++;
       }
}
複製代碼



發佈了12 篇原創文章 · 獲贊 12 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章