標籤:
http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html
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調用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來實現訊息非同步處理等等,應用情境很多如何用的好用的合理,這就沒什麼經驗了。
至於如何使用,源碼中很多例子可以看一下AsyncQueryHandler這個類,其中兩個線程,
完成查詢工作,通過Handler進行線程之間有訊息傳遞。感覺這個利用的很好很巧妙。
參考文章:http://blog.csdn.net/maxleng/article/details/5552976
Android中訊息系統模型和Handler Looper