Android中消息系統模型和Handler Looper


      Android中消息系統模型和Handler Looper

  作為Android中大量使用的Handler,結合Thread使其具有眾多的使用形式和方法,

讓我一時感覺這個東西有些玄乎,不明所以然,這到底是一個什么樣的存在呢?通過網上

資料和源碼的學習,這個Handler也差不多弄清楚了,現在總結下這個學習結果。

 

一 Handler作用和概念

通過官方文檔了解到Handler的大致概念是:

  Handler能夠讓你發送和處理消息,以及Runnable對象;每個Handler對象對應一個Thread和

Thread的消息隊列。當你創建一個Handler時,它就和Thread的消息隊列綁定在一起,然后就可以

傳遞消息和runnable對象到消息隊列中,執行消息后就從消息隊列中退出。

 

  Handler的作用就是:調度消息和runnable對象去被執行;使動作在不同的線程中被執行。

  當一個應用程序中進程被創建時,它的主線程專門運行消息隊列(messageQueue),去管

理頂層的應用程序相關的對象如:activity,broadcastReceiver,windows等,你可以創建你

的Thread,和主線程進行交互——通過Handler,交互的方法就是通過post或者sendMessage。

但是在你的新線程中,給定的Message或者Runnable,會在適當的時候的被調度和處理。

(即不會被立即處理——阻塞式)。

  這是官方文檔中對Handler描述的大致意思(英文比較爛翻譯不定正確)。

 

從這些文檔中我們大概了解到handler干了些什么:

  •   運行在某個線程上,共享線程的消息隊列;
  •   接收消息、調度消息,派發消息和處理消息;
  •   實現消息的異步處理;

基本上就是和消息有關,那么這實際上是在干什么呢?

  ——建立消息處理模型/系統。

  

  要學習Handler,看到肯定是和消息有關,可能還是需要先熟悉一下消息系統的構成和簡單原理。

下面就先學習一下消息系統的基本原理。

二 消息系統的基本原理和構成

       從一般的消息系統模型的建立大致構成以下幾個部分:

    l  消息原型

    l  消息隊列

    l  發送消息

    l  消息循環

    l  消息獲取

    l  消息派發

    l  消息處理

大致模型圖如下:

 

    

 

 

  消息系統模型一般會包括以上七個部分(消息原型,消息隊列,消息發送,消息循環,消息獲取,

消息派發,消息處理)。實際上的核心是消息隊列和消息循環,其余部分都是圍繞這兩部分進行的。

  從前面文檔的分析中我們知道Handler就是用來建立消息處理的系統模型,那么和這里基本消息

系統模型相比,那么Handler又是如何囊括這七個部分的呢?

  在Android中對這六個部分進行了抽象成四個獨立的部分:

    Handler,Message,MessageQueue,Looper;

  •   Message就是消息原型,包含消息描述和數據,
  •   MessageQueue就是消息隊列,
  •   Looper完成消息循環
  •   Handler就是駕馭整個消息系統模型,統領Message,MessgeQueue和Looper;

  

  Handler能夠實現消息系統模型,那么具體是如何進行工作的呢,下面探究一下這其中工作的方法和原理。

 

三 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調用MessageQueueenqueueMessage方法:

       

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

 

五 Handler的應用

  以上這就是整個Handler作用及消息系統模型的建立。

使用也非常簡單,雖然有很多方式,但只要理解Handler是建立在Looper上,實現Thread的

消息系統處理模型,實現消息異步處理,我想對與Handler基本應用上沒有什么不能理解的了。

其他方面可以去看源碼了。

  Handler使用起來是非常簡單的,關鍵就是如何利用消息的異步處理,來合理的完成我們

需要功能和任務。對於一個Thread,我們使用好幾個Handler來進行異步處理,也可以創建新的Thread,

通過Handler來實現消息異步處理等等,應用場景很多如何用的好用的合理,這就沒什么經驗了。

  至於如何使用,源碼中很多例子可以看一下AsyncQueryHandler這個類,其中兩個線程,

完成查詢工作,通過Handler進行線程之間有消息傳遞。感覺這個利用的很好很巧妙。

 

參考文章:http://blog.csdn.net/maxleng/article/details/5552976


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM