Android中HandlerThread的使用及原始碼解析
關於Hanlder的基本使用可以參見博文《Android中Handler的使用》,如果想了解Handler、Looper、Thread等的相互關係以及內部實現原理可以參見博文《深入原始碼解析Android中的Handler,Message,MessageQueue,Looper》。
Android中的API中對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可以被用來建立hanlder物件。需要注意的是start方法必須要呼叫。
先拋開HanlderThread,我們不用這個類看看怎麼使用Handler、Thread、Looper。
我們可以通過Looper.myLooper()方法得到當前執行緒所關聯的looper物件。在建立一個新執行緒的時候,初始情況下新執行緒是沒有關聯looper以及對應的訊息佇列MessageQueue的,對外表現出來就是在該新執行緒中呼叫Looper.myLooper()返回null。如果我們沒有意識到這一點,那麼我們在新執行緒中使用Handler肯能就會遇到問題。
假設為了在新執行緒中使用使用Handler,我們可能會寫出如下的程式碼:
class TestThread extends Thread {
public Handler mHandler;
public void run() {
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
}
}
但是在實際執行的時候會發現當執行到mHandler = new Handler()這一句時就會丟擲異常:
Can’t create handler inside thread that has not called Looper.prepare()
之所以會丟擲異常,可參見Handler建構函式的原始碼。
丟擲異常的原因是: 我們在建構函式中沒有傳遞Looper,這樣Hanlder在建構函式中就使用預設的looper,預設的looper是通過呼叫Looper.myLooper()得來的。當我們呼叫了Looper.prepare()之後,我們就會將looper關聯到當前執行緒中。因此只有在呼叫了Looper.prepare()這個方法之後,Looper.myLooper()才能得到looper物件。所以這裡提示我們要先呼叫Looper.prepare()方法才行。
為了能在新執行緒中正常建立使用Handler,我們將程式碼改成如下所示:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//此處處理訊息
}
};
Looper.loop();
}
}
我們在新執行緒的run方法中,首先呼叫了Looper.prepare()方法,這樣就將looper物件關聯到當前執行緒中了,然後執行new Handler(),在Hanlder的建構函式內部會呼叫Looper.myLooper()得到當前執行緒所關聯的looper物件。在建立完Hanlder物件之後,我們需要呼叫Looper.loop()方法讓訊息佇列迴圈起來。
通過上面的程式碼我們就可以在一個新執行緒中建立並使用Handler物件了,但是問題是每次這麼寫感覺很羅嗦,不方便。為了讓能開發者更方便地在新執行緒中建立並使用Handler,Android提供了HandlerThread這個類,HandlerThread是繼承自Thread類的。
使用HandlerThread的示例程式碼如下:
HandlerThread handlerThread = new HandlerThread("TestHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
public void handleMessage(Message msg) {
//此處處理訊息
};
};
我們建立了HandlerThread之後需要先呼叫其start方法,呼叫start方法之後,run方法就會在HanlderThread執行緒中執行了。
HandlerThread這個類的run方法的原始碼如下所示:
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.myLooper()方法得到該執行緒所關聯的looper物件,最後會呼叫Looper.loop()方法讓訊息佇列迴圈起來。由此可以看出,HandlerThread的run方法主要就是將我們上面給出的正常情況下在新執行緒中建立Handler的程式碼做了一些封裝而已。 在建立HandlerThread物件並呼叫其start方法之後,該HandlerThread執行緒就已經關聯了looper物件(通過Looper.prepare()方法關聯),並且該執行緒內部的訊息佇列迴圈了起來(通過Looper.loop()方法)。 最後我們只需要在建立Handler物件的時候通過handlerThread.getLooper()將handlerThread執行緒所關聯的looper物件傳遞給Handler的建構函式即可。 正如本文開頭API對HandlerThread所解釋的那樣: HandlerThread類可以很方便地建立一個帶有looper的新執行緒。該looper可以被用來建立hanlder物件。需要注意的是start方法必須要呼叫。
HandlerThread使用起來之所以感覺方便,是因為HandlerThread這個類在run方法內部對Looper做了一些工作(呼叫Looper.prepare()和Looper.loop()方法),這樣我們開發者在使用的時候就不需要太多的與Looper打交道了,從而提升開發的便利性。HandlerThread並不是很高深的,只是對我們常見的開發流程做了封裝而已,因此我們不用HandlerThread而自己去實現也是可以的,具體用不用HandlerThread根據自己的喜好而定。
相關文章
- Android 進階之HandlerThread 使用場景及原始碼解析Androidthread原始碼
- HandlerThread原始碼解析thread原始碼
- HandlerThread和IntentService原始碼解析threadIntent原始碼
- Android中IntentService的使用及其原始碼解析AndroidIntent原始碼
- Android 非同步任務知識梳理(2) HandlerThread 原始碼解析Android非同步thread原始碼
- Android原始碼解析Handler系列第(五)篇 ---HandlerThread你用過嗎?Android原始碼thread
- Android IntentService使用全面介紹及原始碼解析AndroidIntent原始碼
- GYHttpMock:使用及原始碼解析HTTPMock原始碼
- TextWatcher的使用及原始碼解析原始碼
- Android 中的 HandlerThread 詳解Androidthread
- 原始碼解析Android中AsyncTask的工作原理原始碼Android
- 終止Android中HandlerThread的方法Androidthread
- Android HandlerThread使用總結Androidthread
- Picasso原始碼分析(三):快照功能實現和HandlerThread的使用原始碼thread
- Android中Loader及LoaderManager的使用(附原始碼下載)Android原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Spring中AOP相關的API及原始碼解析SpringAPI原始碼
- redis中跳錶的運用及原始碼解析(一)Redis原始碼
- redis中跳錶的運用及原始碼解析(二)Redis原始碼
- android原始碼解析--AlertDialog及AlertDialog.BuilderAndroid原始碼UI
- Android Handler 原始碼解析Android原始碼
- Android Retrofit原始碼解析Android原始碼
- Android——LruCache原始碼解析Android原始碼
- android LruCache原始碼解析Android原始碼
- Android EventBus原始碼解析Android原始碼
- android原始碼解析--switchAndroid原始碼
- Android原始碼解析--LooperAndroid原始碼OOP
- 原始碼解析Android中View的layout佈局過程原始碼AndroidView
- Lru-k在Rust中的實現及原始碼解析Rust原始碼
- Lfu快取在Rust中的實現及原始碼解析快取Rust原始碼
- Android 開源專案原始碼解析 -->PhotoView 原始碼解析(七)Android原始碼View
- 深入原始碼解析Android中的Handler,Message,MessageQueue,Looper原始碼AndroidOOP
- 原始碼解析Android中View的measure量算過程原始碼AndroidView
- Android原始碼解析-LiveDataAndroid原始碼LiveData
- [Android] Retrofit原始碼:流程解析Android原始碼
- Android 8.1 Handler 原始碼解析Android原始碼
- Android LayoutInflater 原始碼解析Android原始碼
- WebRTC-Android原始碼解析WebAndroid原始碼