Handler有何作用?如何使用?

轉自:AndroidChina » Handler有何作用?如何使用?


一  Handler作用和概念

包含線程隊列和消息隊列,實現異步的消息處理機制,跟web開發的ajax有異曲同工之妙。
  1. 運行在某個線程上,共享線程的消息隊列;
  2. 接收消息、調度消息,派發消息和處理消息;
  3. 實現消息的異步處理;

Handler能夠讓你發送和處理消息,以及Runnable對象;每個Handler對象對應一個Thread和Thread的消息隊列。當你創建一個Handler時,它就和Thread的消息隊列綁定在一起,然後就可以傳遞消息和runnable對象到消息隊列中,執行消息後就從消息隊列中退出。

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

當一個應用程序中進程被創建時,它的主線程專門運行消息隊列(messageQueue),去管理頂層的應用程序相關的對象如:activity,broadcastReceiver,windows等,你可以創建你的Thread,和主線程進行交互——通過Handler,交互的方法就是通過post或者sendMessage。但是在你的新線程中,給定的Message或者Runnable,會在適當的時候的被調度和處理。

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

實際上就是建立消息處理模型/系統

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

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

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

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

  • 消息原型
  • 消息隊列
  • 發送消息
  • 消息循環
  • 消息獲取
  • 消息派發
  • 消息處理

大致模型圖如下:

消息系統模型一般會包括以上七個部分(消息原型,消息隊列,消息發送,消息循環,消息獲取,消息派發,消息處理)。實際上的核心是消息隊列和消息循環,其餘部分都是圍繞這兩部分進行的。

從前面文檔的分析中我們知道Handler就是用來建立消息處理的系統模型,那麼和這裏基本消息系統模型相比,那麼Handler又是如何囊括這七個部分的呢?

在Android中對這六個部分進行了抽象成四個獨立的部分(消息循環):

Handler,Message,MessageQueue,Looper;

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

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

三 Handler工作原理分析

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

1,Looper:

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

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

2,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,需要做以下工作:

  1. 獲取Thread中的Looper交給Handler的成員變量引用維護;
  2. 通過Looper獲取MessageQueue交給Handler的成員變量引用維護。

那麼消息系統模型建立完成之後,按照消息系統運行,從Handler來看就是發送消息派發消息,與此線程消息系統的交互都由Handler完成。

消息發送和派發接口:

  1. post(runnable)消息,Runnable是消息回調,經過消息循環引發消息回調函數執行;
  2. sendMessage(Message)消息,經過消息循環派發消息處理函數中處理消息;
  3. 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++; } }五 Handler的應用

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

使用也非常簡單,雖然有很多方式,但只要理解Handler是建立在Looper上,實現Thread的消息系統處理模型,實現消息異步處理,我想對與Handler基本應用上沒有什麼不能理解的了。其他方面可以去看源碼了。

Handler使用起來是非常簡單的,關鍵就是如何利用消息的異步處理,來合理的完成我們需要功能和任務。對於一個Thread,我們使用好幾個Handler來進行異步處理,也可以創建新的Thread,通過Handler來實現消息異步處理等等,應用場景很多如何用的好用的合理,這就沒什麼經驗了。



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