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進行線程之間有消息傳遞。感覺這個利用的很好很巧妙。