本文我們將學習HandlerThread的實現原理,以及開發時,如何正確的使用它來實現我們的開發任務。
HandlerThread源碼分析
設想這樣一個場景:我們要在一個線程A中處理業務邏輯,在另一個線程B中,監聽A的執行,並進行結果處理。這時我們使用HandlerThread就可以非常簡單的實現該功能了。
通常我們的線程交互場景,都是UI線程中啓動子線程,並且由子線程完成工作任務,最終結果交給UI線程。現在我們的使用場景是,在子線程中監控其他線程的執行結果(這裏的其他線程可以是另一個子線程,也可以是UI線程),並在子線程中進行結果的處理。
通過描述,我們可以得出2點結論:第一,這個過程中,需要存在2個線程;第二,這兩個線程需要進行數據傳輸(交互)。那麼,我們很自然的就想到了Handler機制來實現該功能,但是我們自己在一個子線程中,使用Handler稍顯麻煩一些,HandlerThread內置了Handler,簡化了我們的操作。
HandlerThread構造函數和繼承關係:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {//構造函數1
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {//構造函數2
super(name);
mPriority = priority;
}
}
邏輯解析:
- HandlerThread繼承自Thread類,所以它本質上是一個線程類,可以實現線程相關的操作。
- 我們來看HandlerThread的構造函數,這裏有2個重載版本。
- 第一個構造函數接收一個name參數,直接調用了super方法,爲該線程命令,且線程優先級爲Process.THREAD_PRIORITY_DEFAULT。
- 第二個構造函數接收2個參數,可實現線程命名,和設置線程的優先級。
HandlerThread的run方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
邏輯解析:
- 我們在創建了HandlerThread實例之後,調用start()方法執行,ART虛擬機會幫我們創建一個線程對象,然後在子線程中執行run方法。
- run方法中,執行了Looper.prepare()方法,創建了一個Looper對象並綁定到該線程。
- 然後在同步塊中,執行了mLooper的賦值,調用notifyAll通知Looper已創建完成。
- 調用Process.setThreadPriority方法設置線程優先級。
- 調用空方法onLooperPrepared,通常用於回調使用。
- 最後調用Looper.loop()方法,啓動該線程的消息循環。
getLooper方法:
public Looper getLooper() {
if (!isAlive()) {//判斷線程是否存活
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
//如果線程存活,但mLooper沒有創建完成,則等待
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;//返回當前線程的Looper對象
}
邏輯解析:
- getLooper方法可以返回當前線程綁定的Looper對象,我們可以使用該對象作爲參數,創建一個該線程的Handler對象,用於線程交互。
- getLooper方法首先會判斷當前線程是否存活,如果存活,則繼續。
- 如果線程存活,但Looper對象還沒有創建完成,則調用wait方法進行等待(Looper創建完成後,會在HandlerThread的run方法中,調用notifyAll()通知,以繼續執行當前邏輯)。
- 最後返回Looper對象即可。
HandlerThread的getThreadHandler方法
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
- 如果我們不想自己創建Handler對象,也可以使用HandlerThread爲他們提供的Handler對象。
- getThreadHandler方法返回一個當前線程的Handler對象。
- 它是一個隱藏方法,我們在應用中不可調用。
HandlerThread的退出
//立即執行退出
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//處理完成已到執行時間的消息後退出。
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
當不需要HandlerThread時,需要調用quit方法或quitSafely方法結束線程管理的Looper消息循環。
HandlerThread實戰
HandlerThread的實現其實並不複雜,我們以一個簡單Demo來看它的使用。
Demo
public class HandlerThreadTest {
private final static String TAG = "budaye";
public static final int HT_MSG = 1;
private HandlerThread mHandlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
private Handler mHandler = null;
public void startHandlerthread(){
mHandlerThread.start();
if (mHandler == null){
mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case HT_MSG:
Log.d(TAG, "當前線程:" + Thread.currentThread());
Log.d(TAG, "收到其他線程發送過來的消息了");
break;
}
}
};
}
}
public void sendMessage() {
Log.d(TAG, "當前線程:" + Thread.currentThread());
Message msg = Message.obtain();
msg.what = HT_MSG;
mHandler.sendMessage(msg);
}
}
邏輯解析:
- 在HandlerThreadTest裏,創建了一個HandlerThread對象。
- 調用startHandlerthread方法,開始了HandlerThread對象的線程消息循環,並且創建了一個mHandler對象,用於處理其他線程發送過來的消息。
- 我們可以在UI線程中調用sendMessage方法來發送給HandlerThread線程一個消息,並在mHandler對象的handleMessage方法中進行處理。
Demo日誌輸出
25208-25208/com.example.simpledemo D/budaye: 當前線程:Thread[main,5,main]
25208-25284/com.example.simpledemo D/budaye: 當前線程:Thread[myHandlerThread,5,main]
25208-25284/com.example.simpledemo D/budaye: 收到其他線程發送過來的消息了
我們分別在2個線程中輸出了日誌。
最佳實踐&總結
- HandlerThread是一個異步處理的工具類,它可以幫助我們很輕鬆的實現異步線程處理。
- HandlerThread繼承自Thread類,它的本質是一個線程類。
- HandlerThread實現原理非常簡單,它利用了Handler原理,在內部了一個Looper循環,並綁定到當前線程中。
- 我們使用創建一個Handler對象,綁定到HandlerTHread對象所對應的Looper,並處理其他線程發送過來的消息。
- HandlerThread在構造方法中可以設置線程優先級,默認使用Process.THREAD_PRIORITY_DEFAULT作爲默認優先級。
- 我們在應用過程中,不要大量使用HandlerThread來執行異步任務,這樣會造成線程資源的浪費,大量的異步任務,建議使用線程池來進行操作。
- HandlerThread的線程優先級設定一定要注意,如果任務優先級不高,則應該設置成後臺任務優先級,避免和UI線程搶佔系統資源。