面試官:能說說HandlerThread的原理和使用場景嗎?

初次看到HandlerThread的名字,我們可能會聯想到Handler和Thread這兩個類,沒錯,它其實就是跟Handler和Thread有莫大的關係。HandlerThread繼承自Thread,它本質上就是一個Thread,而且專門用來處理Handler的消息。

一、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對象可以用於創建Handler類來進行來進行調度,而且start()方法必須被調用。

在Android開發中,不熟悉多線程開發的人一想到要使用線程,可能就用new Thread(){…}.start()這樣的方式。實質上在只有單個耗時任務時用這種方式是可以的,但若是有多個耗時任務要串行執行呢?那不得要多次創建多次銷燬線程,這樣導致的代價是很耗系統資源,容易存在性能問題。那麼,怎麼解決呢?

我們可以只創建一個工作線程,然後在裡面循環處理耗時任務,創建過程如下:

<code>Handler mHandler;

private

void

createWorkerThread

()

{

new

Thread() {

public

void

run

()

{

super

.run(); Looper.prepare(); mHandler =

new

Handler(Looper.myLooper()) {

public

void

handleMessage

(Message msg)

{ ...... } }; Looper.loop(); } }.start();}複製代碼/<code>

在該工作線程中:

調用 Looper.prepare()創建與當前線程綁定的Looper實例;使用上面創建的Looper生成Handler實例;調用Looper.loop()實現消息循環;

然後透過Looper的循環,在Handler的handlerMessage()中進行異步任務的循環處理。而這也正好是HandlerThread的實現。

<code>

void

run

()

{ mTid = Process.myTid(); Looper.prepare();

synchronized

(

this

) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -

1

;}複製代碼/<code>HandlerThread本質上是一個線程類,它繼承了Thread;HandlerThread有自己的內部Looper對象,通過Looper.loop()進行looper循環;通過獲取HandlerThread的looper對象傳遞給Handler對象,然後在handleMessage()方法中執行異步任務;創建HandlerThread後必須調用HandlerThread.start()方法來啟動線程。

二、HandlerThread使用步驟

1、創建HandlerThread實例對象

<code>HandlerThread handlerThread = new HandlerThread("

Handler

Thread

");//HandlerThread handlerThread = new HandlerThread("

Handler

Thread

",Process.THREAD_PRIORITY_DEFAULT);複製代碼

/<code>

HandlerThread默認有兩個構造函數,提供了線程名參數和線程優先級參數的設置。

2、啟動HandlerThread線程

<code>

handlerThread

.start

();複製代碼/<code>

通過start()方法就可以啟動一個HandlerThread了,該線程會不斷地循環運行。

3、通過Handler構建循環消息處理機制

<code>Handler workderHandler =

new

Handler(handlerThread.getLooper(),

new

Handler.Callback() {

public

boolean

handleMessage

(Message msg)

{ /<code>

通過將HandlerThread綁定的Looper對象傳遞給Handler作為參數,構建一個異步的Handler對象,為了能實現耗時任務的異步執行,我們重寫了Handler的Callback接口的handleMessage()方法,當然也可以不重寫該方法,而通過post()方法進行耗時任務操作。

<code>Handler workderHandler =

new

Handler(handlerThread.getLooper());workderHandler.post(

new

Runnable() {

public

void

run

()

{ /<code>

最後,我們就可以通過調用workerHandler以發送消息的形式發送耗時任務到工作線程HandlerThread中去執行,實際上就是在Handler.Callback裡的handleMessage()中執行。

這裡要注意,在創建Handler作為HandlerThread線程消息執行者的時候必須先調用start()方法,因為創建Handler所需要的Looper參數是從HandlerThread中獲得的,而Looper對象的賦值又是在HandlerThread的run()方法中創建。

三、HandlerThread使用實例

<code>

import

android.app.Activity;

import

android.os.Bundle;

import

android.os.Handler;

import

android.os.Message;

import

android.view.View;

import

android.widget.Button;

import

android.widget.TextView;

public

class

HandlerThreadActivity

extends

Activity

implements

Handler

.

Callback

{

private

DBHandlerThread mDBHandlerThread;

private

Handler mUIHandler; /<code>

DBHandlerThread類如下:

<code>

import

android.os.Handler;

import

android.os.HandlerThread;

import

android.os.Message;

public

class

DBHandlerThread

extends

HandlerThread

implements

Handler

.

Callback

{

public

static

final

int

MSG_QUERY_FRIENDS =

100

;

private

Handler mWorkerHandler; /<code>

四、HandlerThread源碼解析

HandlerThread的源碼不多,先看下它的構造函數:

<code>

public

class

HandlerThread

extends

Thread

{ int mPriority; /<code>

從代碼中得知,HandlerThread帶有兩個構造函數,可傳遞兩個參數,一個參數是name,指的是線程的名稱,另一個參數是priority,指的是線程優先級。線程的優先級的取值範圍為-20到19。優先級高的獲得的CPU資源更多,反之則越少。-20代表優先級最高,19最低。

我們可以根據自己的需要去設置線程的優先級,也可以採用默認的優先級,HandlerThread的默認優先級是
Process.THREAD_PRIORITY_DEFAULT,具體值為0。該優先級是再run()方法中設置的,我們看它的run()方法:

<code>

public

class

HandlerThread

extends

Thread

{

protected

void

onLooperPrepared

()

{ }

public

void

run

()

{ mTid = Process.myTid(); /<code>

run()方法主要是通過Looper.prepare()和Looper.loop()構造了一個循環線程。這裡要注意,在創建HandlerThread對象後必須調用其start()方法才能進行run()方法體的執行。

在Looper.prepare()執行後,Looper對象會被創建,然後通過同步鎖機制,將Looper對象賦值給HandlerThread的內部變量mLooper,並通過notifyAll()方法去喚醒等待線程。接著為線程賦予優先級,然後執行onLooperPrepared()方法,該方法是一個空實現,留給我們必要的時候去重寫的,主要用來做一些初始化工作。最後通過執行Looper.loop()在線程中啟動消息隊列。

我們看到在run()方法中進行了喚醒等待線程,為什麼要這麼做呢?答案就在getLooper()方法中:

<code>

public

Looper

getLooper

() {

if

(!isAlive()) { /<code>

該方法用來獲取當前子線程HandlerThread所關聯的Looper對象實例。首先判斷HandlerThread線程是否存活,如果沒有存活就直接返回null,否則繼續執行,進入同步塊並判斷Looper對象是否為空以及線程是否啟動,若都滿足,則調用wait()方法進入阻塞階段,直到Looper對象被成功創建並且通過notifyAll()方法喚醒該等待線程,最後才返回該Looper對象。

Looper對象的創建是在run()方法進行的,也即在子線程中執行的,而getLooper()方法是在UI線程中調用的,若不使用等待喚醒機制,我們就無法保證在UI線程中調用getLooper()方法時Looper對象已經被創建,會面臨一個同步的問題,所以HandlerThread就通過等待喚醒機制來解決該同步問題。

HandlerThread既然是一個循環線程,那麼怎麼退出呢?有兩種方式,分別是不安全的退出方法quit()和安全的退出方法quitSafely():

<code>

public

boolean

quit

()

{ Looper looper = getLooper();

if

(looper !=

null

) { looper.quit();

return

true

; }

return

false

;}複製代碼/<code>

<code>

public

boolean

quitSafely

()

{ Looper looper = getLooper();

if

(looper !=

null

) { looper.quitSafely();

return

true

; }

return

false

;}複製代碼/<code>

通過查看開發者文檔知道,quit()方法在API Level 5時被添加,而quitSafely()方法在API Level 18時才被添加。

quit()方法,主要是把MessageQueue中所有的消息全部清空,無論是延遲消息還是非延遲消息。

而quitSafely()方法只會清空MessageQueue中所有的延遲消息,並將所有的非延遲消息繼續分發出去,最後等到Handler處理完後才停止Looper循環。

這裡的延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發送的消息。

五、HandlerThread應用場景

HandlerThread比較適用於單線程+異步隊列的場景,比如IO讀寫操作數據庫、文件等,耗時不多而且也不會產生較大的阻塞。對於網絡IO操作,HandlerThread並不適合,因為它只有一個線程,還得排隊一個一個等著。

這裡單線程+異步隊列模型的使用場景還舉幾個例子:


應用剛啟動時的數據初始化操作,如果開啟多個線程同時執行,有可能爭奪UI線程的CPU執行時間,造成卡頓,而使用該模型,通過設置優先級就可以將同步工作順序的執行,而又不影響UI的初始化;2.從數據庫中讀取數據展現在ListView中,通過Handler的postAtFrontOfQueue方法,快速將讀取操作加入隊列前端執行,必要時返回給主線程更新UI;3.HandlerThread應用在IntentService中,IntentService是一個需要長時間執行任務Service,優先級要比線程高。

最後

有需要更多資料的朋友可以

私信我【進階】我可以分享給你。

直接點擊即可領取

Android學習PDF+架構視頻+面試文檔+源碼筆記

如果你有其他需要的話,也可以在 GitHub 上查看,下面的資料也會陸續上傳到Github

330頁PDFAndroid核心筆記