一、消息機制使用場景
每個 Android應用程序都運行在一個dalvik虛擬機進程中,進程開始的時候會啟動一個主線程(MainThread),主線程負責處理和ui相關的事 件,因此主線程通常又叫UI線程。而由于Android采用UI單線程模型,所以只能在主線程中對UI元素進行操作。如果在非UI線程直接對UI進行了操 作,則會報錯:Called From Wrong Thread Exception : only the original thread that created a view hierarchy can touch its views。?對于運算量較大的操作和IO操作,我們需要新開線程來處理這些繁重的工作,以免阻塞UI線程。Android為我們提供了消息循環的機制,我們可以利用這個機制來實現線程間的通信。那么,我們就可以在非UI線程發送消息到UI線程,最終讓UI線程來進行UI的操作。
二、消息機制架構
上面是消息機制的框架圖。從圖中看出,消息機制主要有四個核心類:Looper,Handler,Message和Message Queue。
1) ?Looper是消息循環類,它包括了mQueue成員變量;mQueue是消息隊列MessageQueue的實例。Looper還包含了loop()方法,通過調用loop()就能進入到消息循環中。
2)
Handler是消息句柄類。Handler提供了sendMessage()來向消息隊列發送消息;發送消息的API有很多,它們的原理都是一樣的,
這里僅僅只列舉了sendMessage()一個。
此外,Handler還提供了handleMessage()來處理消息隊列的消息;這樣,用戶通過覆蓋
handleMessage()就能處理相應的消息。
3)
Message是消息類。Message包含了next,next是Message的實例;由此可見,Message是一個單鏈表。Message還包
括了target成員,target是Handler實例。此外,它還包括了arg1,arg2,what,obj等參數,它們都是用于記錄消息的相關內
容。
4) ?MessageQueue是消息隊列類,它包含了mMessages成員;mMessages是消息Message的實例。MessageQueue提供了next()方法來獲取消息隊列的下一則消息。
當
我們啟動一個應用程序時,ActivityManagerService會為我們的Activity創建并啟動一個主線程(ActivityThread
對象);在啟動主線程時,就會創建主線程對應的消息循環,并通過調用loop()進入到消息循環中。當我們需要往消息隊列發送消息時,可以繼承
Handler類,然后創建Handler類的實例;接著,通過該實例的sendMessage()方法就可以向消息隊列發送消息。
也就是說,主線程的消息隊列也一直存在的。當消息隊列中沒有消息時,消息隊列會進入空閑等待狀態;當有消息時,則消息隊列會進入運行狀態,進而將相應的消
息發送給handleMessage()進行處理。
ActivityThread創建代碼如下:
通
常在新打開一個應用程序界面時,ActivityManagerService會為該應用啟動一個新的新建ActivityThread作為該改應用的主
線程。該主線程的main函數主要做了兩件事:1.新建ActivityThread對象。 2.使用主線程進入消息循環。
三、關鍵類詳細介紹
1) Looper-線程的魔法師
Looper成員變量
Looper.prepare() && Looper.prepareMainLooper()
通過源碼,prepare()背后的工作方式一目了然,其核心就是將looper對象定義為ThreadLocal。每個線程只能有一個looper。
Looper.loop()
調用loop方法后,Looper線程就開始真正工作了,它不斷從自己的MQ中取出隊頭的消息(也叫任務)執行。
Looper總結有如下幾點:
每個線程有且最多只能有一個Looper對象,它是一個ThreadLocal
Looper內部有一個消息隊列,loop()方法調用后線程開始不斷從隊列中取出消息執行
Looper使一個線程變成Looper線程。
2)Handler-異步處理大師
a. handler發送消息
有了handler之后,我們就可以使用post(Runnable),postAtTime(Runnable, long),postDelayed(Runnable, long),sendEmptyMessage(int),sendMessage(Message),sendMessageAtTime(Message, long)和sendMessageDelayed(Message, long)這些方法向MQ上發送消息了。光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最后都被封裝成message對象了。源碼如下:
handler發出的消息有如下特點:
message.target為該handler對象,這確保了looper執行到該message時能找到處理它的handler
post發出的message,其callback為Runnable對象
handler處理消息
消息的處理主要通過dispatchMessage和handlerMessage來實現的,其源碼如下:
3) ?Message-任務封裝
Message
是線程之間傳遞信息的載體,包含了對消息的描述和任意的數據對象。Message中包含了兩個額外的
int字段和一個object字段,這樣在大部分情況下,使用者就不需要再做內存分配工作了。雖然Message的構造函數是public的,但是最好是
使用Message.obtain( )或Handler.obtainMessage(
)函數來獲取Message對象,因為Message的實現中包含了回收再利用的機制,可以提供效率。
Message結構定義:
消息獲取接口:
消息回收接口:
四、ThreadLocal簡介
當
使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,
而不會影響其它線程所對應的副本。從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。
ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:
void set(Object value)
設置當前線程的線程局部變量的值。
public Object get()
該方法返回當前線程所對應的線程局部變量。
public void remove()
將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。
protected Object initialValue()
返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。
set方法實現: