前言
在Android中,默認情況下,一個線程沒有與它相關聯的Looper。也就是說,默認情況下,我們無法創建與該線程相關聯的Handler。HandlerThread是一個繼承自Thread的線程類,它用於啓動一個有Looper的線程。通過這個Looper,我們就可以創建與該線程相關聯的Handler。通過Handler,我們就可以很方便地控制該線程執行我們的耗時任務。在Android系統中,很多地方都使用了HandlerThread。比如,IntentService、WifiScanningService、NetworkStatsService等等。
基礎知識
HandlerThread的工作原理基於Android的消息機制。具體Android的消息機制的工作原理可以閱讀 Android的消息機制源碼分析 這篇文章。
基本用法
HandlerThread的用法非常簡單。具體的示例代碼如下所示:
HandlerThread thread = new HandlerThread("Thread name");
thread.start();
mLooper = thread.getLooper();
mHandler = new Handler(mLooper) {
@Override
public void handleMessage(Message msg) {
// execute task
}
};
首先,創建一個HandlerThread線程,並啓動該線程。然後,獲取該線程的Looper,並使用這個Looper來創建一個與該線程相關聯的Handler。最後,通過這個Handler,我們就可以很方便地控制該線程執行我們的耗時任務。我們可以使用發送 Message 的方法,也可以使用投遞 Runnable 的方法。
注意:
- 在獲取HandlerThread線程的Looper之前一定要記得先啓動線程。
- 在適當的時候要記得調用mLooper.quit()方法來退出Looper消息循環,結束HandlerThread線程。
- Looper從MessageQueue中循環地取出Message來處理,所以HandlerThread線程以隊列的方式來執行多個任務。
源碼分析
我們將按照HandlerThread基本用法的順序來進行源碼分析。
首先,我們要創建一個HandlerThread線程。所以,我們先來看HandlerThread的構造方法。
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
HandlerThread提供了兩個構造方法。其中,name參數用來設置線程名,priority參數用來設置線程的優先級。第一個構造方法設置HandlerThread線程的優先級爲線程的默認優先級。
然後,我們要啓動該線程。所以,接下來我們來看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;
}
在run()方法中,首先調用了Looper.prepare()靜態方法來創建一個與該線程相關聯的Looper。Looper.prepare()靜態方法下面的同步代碼塊用來同步該線程的Looper對象。接着設置了該線程的優先級。最後調用了Looper.loop()靜態方法來啓動該線程的消息循環。Looper.loop()靜態方法是一個死循環,它循環地取出MessageQueue中的Message來處理。
接着,我們就可以獲取該線程的Looper了。所以,接下來我們來看HandlerThread的getLooper()方法。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
如果在啓動線程之前調用了getLooper()方法,那麼該方法將返回null。如果在啓動線程之後調用了getLooper()方法,那麼該方法將阻塞,直到Looper對象創建完畢。所以,我們要先啓動HandlerThread線程,然後再通過getLooper()方法來獲取與該線程相關聯的Looper。獲取到Looper之後,我們就可以創建與該線程相關聯的Handler了。通過Handler,我們就可以很方便地控制該線程執行我們的耗時任務。
最後,在適當的時候我們要調用mLooper.quit()方法來退出Looper消息循環,結束HandlerThread線程。所以,最後我們來看HandlerThread的quit()方法。
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
quit()方法調用了Looper的quit()方法來退出Looper.loop()死循環,結束HandlerThread線程。
總結
HandlerThread的工作原理基於Android的消息機制。通過使用HandlerThread線程,我們可以很方便地控制該線程執行我們的耗時任務。
例子
這裏舉一個簡單的例子來實踐HandlerThread。項目源碼地址:https://github.com/chongyucaiyan/HandlerDemo
我們創建了一個HandlerThread線程,並創建了一個與它相關聯的Handler。通過這個Handler,我們發送Message來控制HandlerThread線程執行耗時任務。具體的代碼和註釋如下所示:
public class MainActivity extends AppCompatActivity {
private static final int DO_TASK1 = 1;
private static final int DO_TASK2 = 2;
private Looper mLooper;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 創建HandlerThread線程
createHandlerThread();
}
private void createHandlerThread() {
// 創建HandlerThread線程,並啓動該線程
HandlerThread thread = new HandlerThread("HandlerThread");
thread.start();
// 獲取線程的Looper,創建與該線程相關聯的Handler
mLooper = thread.getLooper();
mHandler = new MyHandler(mLooper);
// 發送任務消息
sendMessage();
}
private void sendMessage() {
Message message1 = mHandler.obtainMessage(DO_TASK1);
Message message2 = mHandler.obtainMessage(DO_TASK2);
mHandler.sendMessage(message1);
mHandler.sendMessage(message2);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 退出Looper消息循環
mLooper.quit();
}
private static class MyHandler extends Handler {
private static final String TAG = "MyHandler";
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TASK1:
// 執行任務1
doTask1();
break;
case DO_TASK2:
// 執行任務2
doTask2();
break;
default:
break;
}
}
private void doTask1() {
Log.i(TAG, "doTask1(), start");
Log.i(TAG, "doTask1(), thread name is " + Thread.currentThread().getName());
// 模擬耗時任務
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask1(), end");
}
private void doTask2() {
Log.i(TAG, "doTask2(), start");
Log.i(TAG, "doTask2(), thread name is " + Thread.currentThread().getName());
// 模擬耗時任務
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask2(), end");
}
}
}
運行應用,打印的信息如下圖所示:
可以看到:
- 任務是在HandlerThread子線程中執行的。
- 任務2是在任務1執行完畢之後纔開始執行的。這驗證了HandlerThread線程是以隊列的方式來執行多個任務的。
參考
- Android 7.1.1 (API level 25)
- https://developer.android.com/reference/android/os/HandlerThread.html