HandlerThread

Android中為什么要采用Handler機制?Handler的作用是什么?

Android中的UI線程(主線程)是不安全的,一般來說在子線程中進行UI操作會導致UI線程的阻塞,所以Android提供一套Handler機制來實現異步消息處理。Handler機制的作用是:解決多線程并發的問題(協同其他線程工作),接收其他線程的消息并通過接收到的消息更新主UI線程的內容。

Handler機制是由那些部分構成?各個部分有什么作用?

Handler機制由MessageQueue、Looper以及Handler組成。各部分的功能如下:

MessageQueue的作用是依照隊列先進先出的規則存儲由其他線程的Handler發送過來的Message對象,然后Looper會輪詢并獲取這些Message對象。

Looper的作用是去輪詢MessageQueue,當發現MessageQueue中存在Message,Looper就會將該Message傳遞給對應的Handler的handleMessage( )方法進行處理。(Looper就像是一個消息泵,把得到的Message泵給對應的Handler對象)

Handler的作用是發送消息給MessageQueue以及處理從Looper傳遞過來的Message。

子線程要實現Handler機制處理消息跟UI線程之間有什么不同?

UI線程中的MessageQueue以及Looper是默認創建好的,只要創建Handler對象實例并調用handleMessage方法即可進行消息處理,而子線程要自己去自定義Looper才能實現相同的功能。




子線程如何實現與其相關的Handler?

通過自定義的Looper去實現在子線程中處理消息,代碼如下:

public?class?MainActivity?extends?AppCompatActivity?{

????@Override

????protected?void?onCreate(Bundle?savedInstanceState)?{

????????super.onCreate(savedInstanceState);

????????setContentView(new?LinearLayout(this));

????????MyThread?myThread =?new?MyThread();

????????myThread.start();

????????myThread.mHandler.sendEmptyMessage(0x123);

????}


????class?MyThread?extends?Thread?{

????????public?Handler?mHandler;

????????public?Looper?mLooper;


????????@Override

????????public?void?run()?{

????????????//創建Looper的實例對象,此時會創建一條與之對應的MessageQueue

????????????mLooper.prepare();

????????????//在新建的子線程當中創建Handler實例并調用handleMessage方法

????????????mHandler =?new?Handler()?{

????????????????@Override

????????????????public?void?handleMessage(Message?msg)?{

????????????????????//此處不作任何操作

????????????????}

????????????};

????????????//調用Loop()方法,讓創建好的Looper去輪詢MessageQueue

????????????// 在Loop()之后的代碼將不會執行

????????????mLooper.loop();

????????}

????}}

在子線程中使用自定義的Looper去實現Handler機制的消息處理可能會發生什么問題?

實際上,如果我們在子線程中使用自定義的Looper去實現子線程的Handler機制的消息處理功能有可能會因為線程并發的問題導致程序出現NullPointException。在如下實例代碼中,程序會報出NullPointException的Error,Error的根源就是在UI線程中創建Handler實例時傳入的myThread.mLooper對象,由于UI線程與子線程的并發問題,在UI線程使用這個myThread.mLooper對象時子線程有可能還沒給這個對象創建出實例(創建出Looper和MessageQueue),從而使程序拋出NullPointException,實例代碼如下:

public?class?MainActivity?extends?AppCompatActivity?{


????@Override

????protected?void?onCreate(Bundle?savedInstanceState)?{

????????super.onCreate(savedInstanceState);

????????setContentView(new?LinearLayout(this));

????????MyThread?myThread =?new?MyThread();

????????myThread.start();

????????Handler?handler =?new?Handler(myThread.mLooper)?{

????????????@Override

????????????public?void?handleMessage(Message?msg)?{

????????????????//此處不作任何操作

????????????}

????????};

????????handler.sendEmptyMessage(0x123);

????}


????class?MyThread?extends?Thread?{

????????public?Handler?mHandler;

????????public?Looper?mLooper;


????????@Override

????????public?void?run()?{

????????????//創建Looper的實例對象,此時會創建一條與之對應的MessageQueue

????????????mLooper.prepare();

????????????//在新建的子線程當中創建Handler實例并調用handleMessage方法

????????????mHandler =?new?Handler()?{

????????????????@Override

????????????????public?void?handleMessage(Message?msg)?{

????????????????????//此處不作任何操作

????????????????}

????????????};

????????????//調用Loop()方法,讓創建好的Looper去輪詢MessageQueue

????????????// 在Loop()之后的代碼將不會執行

????????????mLooper.loop();

????????}

????}}

在主線程中創建Handler對象的時候傳入myThread.mLooper的作用是為該Handler對象指定一個Looper,myThread.mLooper已經替代UI線程中默認的Looper,也就是說該Hanlder對象發送消息以后會由myThread.mLooper將消息泵給myThread.mHandler的handleMessage方法進行處理。

上述的程序邏輯并無錯誤,但將其運行一遍會發現程序會報出NullpointException。

怎么樣克服子線程自定義Looper帶來的問題呢?

想要避免這個NullPointException最簡單的方法就是try/catch一下(哈哈哈,開玩笑的)。其實Android本來就有一套推薦的方案在子線程中實現Handler機制,不需要再額外地自定義Looper這么麻煩,Android推薦使用HandlerThread來在子線程中實現Handler機制。

HandlerThread是Handler類還是Thread類?它究竟是什么?

HandlerThread其實就是Thread類,它跟普通的Thread相比就是多了一個Looper,但就是多了這么一個Looper讓我們省卻自定義Looper和擔心由于線程并發而造成Looper在未創建成功就被調用的的麻煩。

怎么使用HandlerThread?

HandlerThread的使用方法很簡單,大致分為4步,直接上碼:

public?class?MainActivity?extends?AppCompatActivity?{


????@Override

????protected?void?onCreate(Bundle?savedInstanceState)?{

????????super.onCreate(savedInstanceState);

????????setContentView(new?LinearLayout(this));

????????//第一步,創建HandlerThread對象,并為它指定名稱 ?

????????HandlerThread?handlerThread =?new?HandlerThread("handler_thread");

????????//第二步,調用start()方法開啟線程

????????handlerThread.start();

????????//第三步,創建Handler對象,傳入handlerThread.getLooper()參數

????????Handler?handler =?new?Handler(handlerThread.getLooper())?{

????????????@Override

????????????public?void?handleMessage(Message?msg)?{

????????????????//此處不作任何操作

????????????}

????????};

????????//第四步,發送消息

????????handler.sendEmptyMessage(0x123);

????}}

其實在上述的第四步發送消息的這一步其實并不只是可以用sendEmptyMessage方式,可以新開線程去發送消息,也可以調用post方法以后再發送消息,但最最重要的是要明白,在第三步中,把原本是綁定UI線程Looper的Handler設置成綁定HandlerThread的Looper。所以程序的Handler現在是與HandlerThread進行關聯,這也就達到了在子線程上實現Handler機制的目的??!

從線程通信的角度來看待Handler和HandlerThread分別解決了什么?

Handler的目標是解決子線程與主線程通信的問題,而HandlerThread則是解決子線程和子線程之間的通信問題

最后總結兩句:

MessageQueue、Looper和Handler是組成Handler機制的鐵人三項,雖然我們平時比較少接觸到MessageQueue和Looper,但是其作用,原理以及用法還是很重要的,MessageQueue負責存儲由Handler發送過來的Message,Looper則不斷輪詢MessageQueue,將MessageQueue中的Message交由Handler進行處理,Handler根據Looper傳來的Message進行信息處理或UI更新



觀察HandlerThread的官方文檔的兩句:①Thread. Handy class for starting a new thread that has a looper.②The looper can then be used to create handler classes.

釋義:HandlerThread對象start后可以獲得其Looper對象,并且使用這個Looper對象實例Handler,之后Handler就可以運行在其他線程中了。

HandlerThread繼承于Thread,所以它本質就是個Thread。與普通Thread的差別就在于,然后在內部直接實現了Looper的實現,這是Handler消息機制必不可少的。有了自己的looper,可以讓我們在自己的線程中分發和處理消息。如果不用HandlerThread的話,需要手動去調用Looper.prepare()和Looper.loop()這些方法。

// 子線程中創建新的Handler 沒有使用HandlerThread

new Thread () {

? ? @Override

? ? public void run() {

? ? ? ? Looper.prepare();

? ? ? ? Hnadler handler = new Handler();

? ? ? ? Looper.loop();

? ? }

}

mSMTHandlerThread = new HandlerThread("SMT-Handler-thread");

? ? ? ? mSMTHandlerThread.start();

? ? ? ? mSMTHandler = new Handler(mSMTHandlerThread.getLooper());

Handler里部分源碼:

public Handler() {

? ? ? ? throw new RuntimeException("Stub!");

? ? }

? ? public Handler(Handler.Callback callback) {

? ? ? ? throw new RuntimeException("Stub!");

? ? }

? ? public Handler(Looper looper) {

? ? ? ? throw new RuntimeException("Stub!");

? ? }

? ? public Handler(Looper looper, Handler.Callback callback) {

? ? ? ? throw new RuntimeException("Stub!");

? ? }

?當調用quitSafely方法時,其內部調用的是Looper的quitSafely方法而最終執行的是MessageQueue中的removeAllFutureMessagesLocked方法,

該方法只會清空MessageQueue消息池中所有的延遲消息,并將消息池中所有的非延遲消息派發出去讓Handler去處理完成后才停止Looper循環,

quitSafely相比于quit方法安全的原因在于清空消息之前會派發所有的非延遲消息。最后需要注意的是Looper的quit方法是基于API 1,

而Looper的quitSafely方法則是基于API 18的。

quit()和quitSafely()的區別在于,quit()會清空所有消息(無論延時或非延時),quitSafely()只清空延時消息,無論是調用了quit()方法還是quitSafely()方法,

Looper就不再接收新的消息。即在調用了Looper的quit()或quitSafely()方法之后,消息循環就終結了,

這時候再通過Handler調用sendMessage或post等方法發送消息時均返回false,線程也就結束了

mSMTHandler.removeCallbacks(timeoutRunnable);

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容