初次看到HandlerThread的名字,我們可能會聯想到Handler和Thread這兩個類,沒錯,它其實就是跟Handler和Thread有莫大的關係。HandlerThread繼承自Thread,它本質上就是一個Thread,而且專門用來處理Handler的消息。
一、HandlerThread簡介
看看官方對它的解釋:
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
大致就是說HandlerThread可以創建一個帶有looper的線程,looper對象可以用於創建Handler類來進行來進行調度,而且start()方法必須被調用。
在Android開發中,不熟悉多線程開發的人一想到要使用線程,可能就用new Thread(){…}.start()這樣的方式。實質上在只有單個耗時任務時用這種方式是可以的,但若是有多個耗時任務要串行執行呢?那不得要多次創建多次銷燬線程,這樣導致的代價是很耗系統資源,容易存在性能問題。那麼,怎麼解決呢?
我們可以只創建一個工作線程,然後在裡面循環處理耗時任務,創建過程如下:
<code>Handler mHandler;
private
void
createWorkerThread
()
{new
Thread() {public
void
run
()
{super
.run(); Looper.prepare(); mHandler =new
Handler(Looper.myLooper()) {public
void
handleMessage
(Message msg)
{ ...... } }; Looper.loop(); } }.start();}複製代碼/<code>在該工作線程中:
調用 Looper.prepare()創建與當前線程綁定的Looper實例;使用上面創建的Looper生成Handler實例;調用Looper.loop()實現消息循環;然後透過Looper的循環,在Handler的handlerMessage()中進行異步任務的循環處理。而這也正好是HandlerThread的實現。
<code>
void
run
()
{ mTid = Process.myTid(); Looper.prepare();synchronized
(this
) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1
;}複製代碼/<code>HandlerThread本質上是一個線程類,它繼承了Thread;HandlerThread有自己的內部Looper對象,通過Looper.loop()進行looper循環;通過獲取HandlerThread的looper對象傳遞給Handler對象,然後在handleMessage()方法中執行異步任務;創建HandlerThread後必須調用HandlerThread.start()方法來啟動線程。二、HandlerThread使用步驟
1、創建HandlerThread實例對象
<code>HandlerThread handlerThread = new HandlerThread("
Handler
Thread
");//HandlerThread handlerThread = new HandlerThread("
Handler
Thread
",Process.THREAD_PRIORITY_DEFAULT);複製代碼
/<code>HandlerThread默認有兩個構造函數,提供了線程名參數和線程優先級參數的設置。
2、啟動HandlerThread線程
<code>
handlerThread
.start
();複製代碼/<code>通過start()方法就可以啟動一個HandlerThread了,該線程會不斷地循環運行。
3、通過Handler構建循環消息處理機制
<code>Handler workderHandler =
new
Handler(handlerThread.getLooper(),new
Handler.Callback() {public
boolean
handleMessage
(Message msg)
{ /<code>通過將HandlerThread綁定的Looper對象傳遞給Handler作為參數,構建一個異步的Handler對象,為了能實現耗時任務的異步執行,我們重寫了Handler的Callback接口的handleMessage()方法,當然也可以不重寫該方法,而通過post()方法進行耗時任務操作。
<code>Handler workderHandler =
new
Handler(handlerThread.getLooper());workderHandler.post(new
Runnable() {public
void
run
()
{ /<code>最後,我們就可以通過調用workerHandler以發送消息的形式發送耗時任務到工作線程HandlerThread中去執行,實際上就是在Handler.Callback裡的handleMessage()中執行。
這裡要注意,在創建Handler作為HandlerThread線程消息執行者的時候必須先調用start()方法,因為創建Handler所需要的Looper參數是從HandlerThread中獲得的,而Looper對象的賦值又是在HandlerThread的run()方法中創建。
三、HandlerThread使用實例
<code>
import
android.app.Activity;import
import
android.os.Handler;import
android.os.Message;import
android.view.View;import
android.widget.Button;import
android.widget.TextView;public
class
HandlerThreadActivity
extends
Activity
implements
Handler
.Callback
{private
DBHandlerThread mDBHandlerThread;private
Handler mUIHandler; /<code>DBHandlerThread類如下:
<code>
import
android.os.Handler;import
android.os.HandlerThread;import
android.os.Message;public
class
DBHandlerThread
extends
HandlerThread
implements
Handler
.Callback
{public
static
final
int
MSG_QUERY_FRIENDS =100
;private
Handler mWorkerHandler; /<code>四、HandlerThread源碼解析
HandlerThread的源碼不多,先看下它的構造函數:
<code>
public
class
HandlerThread
extends
Thread
{ int mPriority; /<code>從代碼中得知,HandlerThread帶有兩個構造函數,可傳遞兩個參數,一個參數是name,指的是線程的名稱,另一個參數是priority,指的是線程優先級。線程的優先級的取值範圍為-20到19。優先級高的獲得的CPU資源更多,反之則越少。-20代表優先級最高,19最低。
Process.THREAD_PRIORITY_DEFAULT,具體值為0。該優先級是再run()方法中設置的,我們看它的run()方法:
<code>
public
class
HandlerThread
extends
Thread
{protected
void
onLooperPrepared
()
{ }public
void
run
()
{ mTid = Process.myTid(); /<code>run()方法主要是通過Looper.prepare()和Looper.loop()構造了一個循環線程。這裡要注意,在創建HandlerThread對象後必須調用其start()方法才能進行run()方法體的執行。
在Looper.prepare()執行後,Looper對象會被創建,然後通過同步鎖機制,將Looper對象賦值給HandlerThread的內部變量mLooper,並通過notifyAll()方法去喚醒等待線程。接著為線程賦予優先級,然後執行onLooperPrepared()方法,該方法是一個空實現,留給我們必要的時候去重寫的,主要用來做一些初始化工作。最後通過執行Looper.loop()在線程中啟動消息隊列。
我們看到在run()方法中進行了喚醒等待線程,為什麼要這麼做呢?答案就在getLooper()方法中:
<code>
public
Looper
getLooper
() {if
(!isAlive()) { /<code>該方法用來獲取當前子線程HandlerThread所關聯的Looper對象實例。首先判斷HandlerThread線程是否存活,如果沒有存活就直接返回null,否則繼續執行,進入同步塊並判斷Looper對象是否為空以及線程是否啟動,若都滿足,則調用wait()方法進入阻塞階段,直到Looper對象被成功創建並且通過notifyAll()方法喚醒該等待線程,最後才返回該Looper對象。
Looper對象的創建是在run()方法進行的,也即在子線程中執行的,而getLooper()方法是在UI線程中調用的,若不使用等待喚醒機制,我們就無法保證在UI線程中調用getLooper()方法時Looper對象已經被創建,會面臨一個同步的問題,所以HandlerThread就通過等待喚醒機制來解決該同步問題。
HandlerThread既然是一個循環線程,那麼怎麼退出呢?有兩種方式,分別是不安全的退出方法quit()和安全的退出方法quitSafely():
<code>
public
boolean
quit
()
{ Looper looper = getLooper();if
(looper !=null
) { looper.quit();return
true
; }return
false
;}複製代碼/<code><code>
public
boolean
quitSafely
()
{ Looper looper = getLooper();if
(looper !=null
) { looper.quitSafely();return
true
; }return
false
;}複製代碼/<code>通過查看開發者文檔知道,quit()方法在API Level 5時被添加,而quitSafely()方法在API Level 18時才被添加。
quit()方法,主要是把MessageQueue中所有的消息全部清空,無論是延遲消息還是非延遲消息。
而quitSafely()方法只會清空MessageQueue中所有的延遲消息,並將所有的非延遲消息繼續分發出去,最後等到Handler處理完後才停止Looper循環。
這裡的延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發送的消息。
五、HandlerThread應用場景
HandlerThread比較適用於單線程+異步隊列的場景,比如IO讀寫操作數據庫、文件等,耗時不多而且也不會產生較大的阻塞。對於網絡IO操作,HandlerThread並不適合,因為它只有一個線程,還得排隊一個一個等著。
這裡單線程+異步隊列模型的使用場景還舉幾個例子:
應用剛啟動時的數據初始化操作,如果開啟多個線程同時執行,有可能爭奪UI線程的CPU執行時間,造成卡頓,而使用該模型,通過設置優先級就可以將同步工作順序的執行,而又不影響UI的初始化;2.從數據庫中讀取數據展現在ListView中,通過Handler的postAtFrontOfQueue方法,快速將讀取操作加入隊列前端執行,必要時返回給主線程更新UI;3.HandlerThread應用在IntentService中,IntentService是一個需要長時間執行任務Service,優先級要比線程高。
最後
有需要更多資料的朋友可以
直接點擊即可領取
Android學習PDF+架構視頻+面試文檔+源碼筆記
如果你有其他需要的話,也可以在 GitHub 上查看,下面的資料也會陸續上傳到Github
330頁PDFAndroid核心筆記