不達成功誓不休 — 程端禮
寫在前面
“送走四月,喜迎五月”,今年的五一小長假簡直舒服到了極致,犧牲兩個周六換來的四天假期。是時候出去浪了, 打開微博發現不妙,嚇得我趕緊躺床上又睡了一覺...
入職后經常加班,僅有的私人時間也沒有心思學習。不過我還是告訴自己在忙也不能忘了充實自己。趁著今天有興致,閱讀了一下HandlerThread的源碼。
用法
首先,我們來了解一下HandlerThread。
Q:什么是HandlerThread?
A:HandlerThread繼承自Thread,可以說其是一個線程。并且有一個Looper對象進行消息循環。
接下來,我們看一下HandlerThread是如何執行異步任務的。
// 創建HandlerThread對象,參數為該線程的Name
HandlerThread handlerThread = new HandlerThread("work");
// 調用HandlerThread的start()函數開啟線程
handlerThread.start();
// 通過調用HandlerThread的getLooper()函數得到Looper對象,并創建Handler用于執行異步任務
Handler workHandler = new Handler(handlerThread.getLooper(), new WorkHandlerCallback());
// 在主線程中創建Handler,用于更新UI(主線程中有一個Looper)
Handler uiHandler = new Handler(new UIHandlerCallback());
// 發送一個異步任務的消息
Message msg = workHandler.obtainMessage();
msg.what = 0x01;
workHandler.sendMessage(msg);
private class WorkHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0x01:
// 開始執行異步任務
int sum = 0;
for (int i = 0 ; i < 100 ; i ++) {
sum += i;
}
// 異步任務執行成功后,告知UI線程更新
Message message = mUiHandler.obtainMessage();
message.what = 0x02;
message.obj = sum + "";
mUiHandler.sendMessage(message);
break;
default:
break;
}
return true;
}
}
private class UIHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0x02:
// 更新UI
textView.setText((String) msg.obj);
// 異步任務做完以后,不再需要HandlerThread,就調用quit()函數退出
handlerThread.quit();
break;
default:
break;
} return false;
}
}
以上就是HandlerThread的基本用法,是不是很簡單呢,通過簡單的幾步就可以在異步線程中做耗時任務,再也不用擔心主線程阻塞了。
注意:
- HandlerThread也是一個線程,想要其工作就一定要調用start()函數,而且Looper對象是在run()函數中創建的,只有run()函數執行了,才會創建Looper對象進行消息循環。
- 通過HandlerThread的getLooper()創建的Handler只能用來執行異步任務,因為它不在主線程中,無法進行UI更新,適合做一些耗時任務。
源碼
學會了使用HandlerThread,那么就不想知道它的內部是如何實現的嗎?反正我是挺想的,也不知道你想不想,咱也不知道,咱也不敢問,有興趣的童鞋就和我一起探其究竟吧。
public class HandlerThread extends Thread {
// 線程優先級
int mPriority;
// 線程id
int mTid = -1;
// 該線程所持有的Looper對象
Looper mLooper;
// 線程內部的Handler,可以通過getThreadHandler()函數直接獲取使用
private @Nullable Handler mHandler;
// 擁有一個參數的構造函數
// 傳入參數為線程名稱,具有默認線程優先級
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
// 擁有兩個參數的構造函數
// 傳入的參數name是線程名稱,參數priority是線程優先級(線程優先級詳見Process類)
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 該函數為空實現,子類可以重寫該函數,在Looper進行消息循環之前調用
protected void onLooperPrepared() {
}
// 調用該線程的start()函數后,run()函數會被執行
// run()函數可以說是HandlerThread的核心,該函數內部會創建Looper進行消息循環
@Override
public void run() {
// 獲取該線程的id
mTid = Process.myTid();
// 為該線程創建Looper
Looper.prepare();
// 通過持有同步鎖機制得到該線程的Looper對象
// 然后調用notifyAll()函數通知getLooper()函數Looper對象已經創建完成。
synchronized (this) {
// 獲取該線程的Looper對象
mLooper = Looper.myLooper();
// 喚醒在當前對象監視器上等待的所有線程
notifyAll();
}
// 設置線程優先級
Process.setThreadPriority(mPriority);
// 調用onLooperPrepared()函數,子類可以在消息循環之前做一些準備工作
onLooperPrepared();
// 開始消息循環
Looper.loop();
mTid = -1;
}
// 獲取該線程的Looper對象
public Looper getLooper() {
// 如果該線程不是isAlive,則直接返回null
if (!isAlive()) {
return null;
}
// 通過持有同步鎖機制判斷當前是否創建了Looper對象
// 如果該線程沒有start,即還沒有創建Looper對象
// 則調用wait()函數,使當前對象上的線程進入等待,就是等待run()函數中的notifyAll()函數執行
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
// 可以直接獲取一個Handler對象
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
// 創建Handler實例,參數為該線程的Looper
mHandler = new Handler(getLooper());
}
return mHandler;
}
// 退出消息循環,效率高,但不是線程安全的
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
// 調用Looper對象的quit()函數退出消息循環,
// 內部調用的是MessageQueue的quit(boolean safe)函數,傳入參數為false
looper.quit();
return true;
}
return false;
}
// 退出消息循環,效率低,但是線程安全的
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
// 調用Looper對象的quitSafely()函數退出消息循環
// 內部調用的是MessageQueue的quit(boolean safe)函數,傳入參數為true
looper.quitSafely();
return true;
}
return false;
}
// 獲取當前線程id
public int getThreadId() {
return mTid;
}
}
總結
分析了HandlerThread的源碼后,總結為以下幾點:
- HandlerThread有兩個構造函數,分別是線程名稱或線程名稱和線程優先級。
- HandlerThread的本質就是一個線程,但不同于線程,它的核心就是run()函數,它在run()函數內部創建一個Looper對象進行消息循環,使通過該Looper創建的Handler實例運行在該線程中。為了避免getLooper()獲取到的Looper對象為空,采用了同步鎖機制,即在getLooper()函數中若mLooper為null,則讓當前對象上的線程進入等待,直到run()函數中創建好Looper對象后,喚醒在當前對象監視器上等待的所有線程,即getLooper()函數繼續執行。
- HandlerThread內部提供了一個Handler,其創建方式也是通過getLooper()函數獲取該線程的Looper對象進行創建,不過該Handler只有在外部調用getThreadHandler()時才會進行創建。
- HandlerThread有兩種退出方式,一種不是線程安全的,但是效率高;一種是線程安全的,但是效率低;其最終調用的都是MessageQueue的quit(boolean safe)函數。
- HandlerThread和Thread一樣,都是調用start()函數開啟線程。
- HandlerThread的構造函數需要傳入線程名稱,而Thread的構造函數則不需要。
- 繼承自Thread的子類需要重寫其run()函數,在其中執行任務;而HandlerThread的子類可以重寫 onLooperPrepared()函數,在創建Looper對象之前做準備工作。