想要瞭解 HandlerThread 的工作原理需要先對 Android 系統中以 Handler、Looper、MessageQueue 組成的非同步消息處理機制有所瞭解,如果你還沒有這方面的知識,可以先看我寫的另一篇文章: "Handler、Looper與MessageQueue源碼解析" 一、概 ...
想要瞭解 HandlerThread 的工作原理需要先對 Android 系統中以 Handler、Looper、MessageQueue 組成的非同步消息處理機制有所瞭解,如果你還沒有這方面的知識,可以先看我寫的另一篇文章:Handler、Looper與MessageQueue源碼解析
一、概述
先來瞭解下 HandlerThread 的幾個特性
- HandlerThread 繼承於 Thread,本身就是一個線程類
- HandlerThread 在內部維護了自己的 Looper 對象,所以可以進行 looper 迴圈
- 創建 HandlerThread 後需要先調用
HandlerThread.start()
方法再向其下發任務,通過run()
方法來創建 Looper 對象 - 通過傳遞 HandlerThread 的 Looper 對象給 Handler 對象,從而可以通過 Handler 來向 HandlerThread 下發耗時任務
二、使用方式
再來看下 HandlerThread 的使用方式
創建 HandlerThread 並調用 start()
方法,使其在子線程內創建 Looper 對象
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
然後以 HandlerThread 內部的 Looper 對象為參數創建一個 Handler,通過 Handler 向子線程發送 Message,以此下發耗時任務,消息的接收者與任務的處理者則由回調函數 ChildCallback 來完成
Handler childThreadHandler = new Handler(handlerThread.getLooper(), new ChildCallback());
//Callback 運行於子線程
private class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在此可以進行耗時操作
//如果需要更新UI,則需要通過主線程的Handler來完成
return false;
}
}
完整的示例代碼如下所示
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//Callback 運行於子線程
private class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在此可以進行耗時操作
//如果需要更新UI,則需要通過主線程的Handler來完成
Log.e(TAG, "ChildCallback 當前線程名:" + Thread.currentThread().getName() + " " + "當前線程ID:" + Thread.currentThread().getId());
Log.e(TAG, "耗時任務開始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "耗時任務結束");
//通知界面更新UI
uiHandler.sendEmptyMessage(1);
return false;
}
}
//運行於主線程的Handler,用於更新UI
@SuppressLint("HandlerLeak")
private Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, "uiHandler 當前線程名:" + Thread.currentThread().getName() + " " + "當前線程ID:" + Thread.currentThread().getId());
Toast.makeText(MainActivity.this, "耗時操作完成", Toast.LENGTH_LONG).show();
}
};
//用於向子線程發佈耗時任務的Handler
private Handler childThreadHandler;
private HandlerThread handlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
childThreadHandler = new Handler(handlerThread.getLooper(), new ChildCallback());
Log.e(TAG, "onCreate 當前線程名:" + Thread.currentThread().getName() + " " + "當前線程ID:" + Thread.currentThread().getId());
}
public void startTask(View view) {
childThreadHandler.sendEmptyMessage(1);
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
childThreadHandler.removeCallbacksAndMessages(null);
}
}
程式運行後的日誌如下所示,可以看出各個方法在調用時所處的線程
06-22 02:51:41.779 21977-21977/com.leavesc.myapplication E/MainActivity: onCreate 當前線程名:main 當前線程ID:2
06-22 02:51:44.927 21977-21995/com.leavesc.myapplication E/MainActivity: ChildCallback 當前線程名:HandlerThread 當前線程ID:497
06-22 02:51:44.928 21977-21995/com.leavesc.myapplication E/MainActivity: 耗時任務開始
06-22 02:51:49.930 21977-21995/com.leavesc.myapplication E/MainActivity: 耗時任務結束
06-22 02:51:49.930 21977-21977/com.leavesc.myapplication E/MainActivity: uiHandler 當前線程名:main 當前線程ID:2
三、源碼分析
先看下 HandlerThread 的類聲明
Thread 的子類
public class HandlerThread extends Thread
兩個構造函數,可以傳入的參數分別是線程名和線程優先順序
public HandlerThread(String name) {
super(name);
//使用預設的線程優先順序
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
//使用自定義的線程優先順序
mPriority = priority;
}
看下其 run()
方法
@Override
public void run() {
mTid = Process.myTid();
//觸發當前線程創建 Looper 對象
Looper.prepare();
synchronized (this) {
//獲取 Looper 對象
mLooper = Looper.myLooper();
//喚醒在等待的線程
//喚醒 getLooer() 中可能還處於等待狀態的線程
notifyAll();
}
//設置線程優先順序
Process.setThreadPriority(mPriority);
onLooperPrepared();
//開啟消息迴圈
Looper.loop();
mTid = -1;
}
Looper.prepare()
方法用於為當前線程創建一個 Looper 對象,在主線程需要依賴此 Looper 對象來構建一個 Handler 對象,通過該 Handler 對象來向子線程下發耗時任務(這些知識點可以在這裡瞭解:Handler、Looper與MessageQueue源碼解析)
之後可以看到有一個同步代碼塊,在當中調用了 notifyAll()
來喚醒等待線程,那該喚醒的又是哪個線程呢?這裡需要明確各個方法是運行於哪個線程,run()
方法肯定是運行於子線程,但用於向 HandlerThread 下發任務的 Handler 是初始化於主線程,因此 getLooper()
方法也是運行於主線程的。由於是兩個不同的線程,run()
方法和 getLooper()
的運行先後順序是不明確的,因此 getLooper()
方法需要確保 Looper 對象不為 null 時才返回,否則將一直阻塞等待 Looper 對象初始化完成
//獲取與當前線程關聯的 Looper 對象
//因為 getLooper() 方法可能先於 run() 被調用,此時就需要先等待 Looper 對象被創建
public Looper getLooper() {
//如果當前線程未在運行,則返回 null
if (!isAlive()) {
return null;
}
synchronized (this) {
//如果當前線程已處理運行狀態(已調用 start() 方法)且 Looper 對象還未創建
//則調用 wait() 方法釋放鎖,等待 Looper 對象創建
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
Looper 對象初始化完成後,就需要調用 Looper.loop()
來開啟消息迴圈,至此 HandlerThread 的初始化操作就完成了
最後再貼一下 HandlerThread 的完整源碼註釋
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
/**
* 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.
*/
public class HandlerThread extends Thread {
//線程優先順序
int mPriority;
//線程ID
int mTid = -1;
//當前線程持有的Looper對象
Looper mLooper;
//包含當前 Looper 對象的 Handler
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
//使用預設的線程優先順序
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
//使用自定義的線程優先順序
mPriority = priority;
}
//在 Looper 迴圈啟動前調用
//此處是空實現,留待子類重寫
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
//觸發當前線程創建 Looper 對象
Looper.prepare();
synchronized (this) {
//獲取 Looper 對象
mLooper = Looper.myLooper();
//喚醒在等待的線程
//喚醒 getLooer() 中可能還處於等待狀態的線程
notifyAll();
}
//設置線程優先順序
Process.setThreadPriority(mPriority);
onLooperPrepared();
//開啟消息迴圈
Looper.loop();
mTid = -1;
}
//獲取與當前線程關聯的 Looper 對象
//因為 getLooper() 方法可能先於 run() 被調用,此時就需要先等待 Looper 對象被創建
public Looper getLooper() {
//如果當前線程未在運行,則返回 null
if (!isAlive()) {
return null;
}
synchronized (this) {
//如果當前線程已處理運行狀態(已調用 start() 方法)且 Looper 對象還未創建
//則調用 wait() 方法釋放鎖,等待 Looper 對象創建
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
//清空消息隊列中所有的消息
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;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
更多的源碼解讀請看這裡:Java_Android_Learn