Android中HandlerThread的使用及原理解析,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根據自己的喜好而定。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。