一、什麼是HandlerThread及特點
HandlerThread本質就是一個Thread,在内部建立了消息隊列機制(Looper、MessageQueue),具有消息循環的線程。
特點
- HandlerThread本質上一個線程類,它繼承了Thread
- HandlerThread與自己的内部Looper對象,可以進行loop循環
- 通過擷取looper對象傳遞給Handler對象,并可以在Handler的handleMessage方法中執行異步任務
- 與線程池重并發不同,handlerThread是一個串行隊列,背後隻有一個線程
- 優點就是不會堵塞,減少對信性能的消耗,缺點是不能同時進行多任務處理,需要等待處理,效率較低。
二、HandlerThread使用方法
1.建立HandlerThread線程,并在構造方法内傳入線程名稱,可以是任意字元串
HandlerThread mHandlerThread = new HandlerThread("Handler_Thread");
2.啟動線程
3.建立異步Handler對象,并建構Callback消息循環機制
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//子線程内運作,此處不能更新UI
Log.d(TAG, "handleMessage: "+msg.what+" 線程名:"+Thread.currentThread().getName());
// mTvContent.setText(String.valueOf(msg.what));
//主線程Hanlder,發送消息通知更新UI
mUIHandler.sendMessage(msg);
return false;
}
});
以上步驟3建構一個完整異步Handler,将HandlerThread的Looper對接和Callback接口類作為參數傳遞到異步 mchildHandler,這樣目前的mchildHandler對象就擁有HandlerThread的Looper對象,由于HandlerThread本身是異步線程,故Looper也綁定到異步線程中,是以handleMessage方法是處理異步耗時任務,當處理狀态發生改變,需要UI線程的Handler發送消息通知更新UI。
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//子線程内運作,此處不能更新UI
Log.d(TAG, "handleMessage: "+msg.what+" 線程名:"+Thread.currentThread().getName());
// mTvContent.setText(String.valueOf(msg.what));
//主線程Hanlder,通知主線程更新
Message message = Message.obtain();
message.what=msg.what;
mUIHandler.sendMessage(message);
return false;
}
});
模拟觸發異步線程
for (int i = ; i <= ; i++) {
Message obtain = Message.obtain();
obtain.what = i;
mchildHandler.sendMessageAtTime(obtain,);
}
三、HandlerThread源碼分析
首先我們從構造函數開始:
public class HandlerThread extends Thread {
//線程優先級
int mPriority;
int mTid = -;
//目前線程所持有的Looper對象
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
從源碼可以看出HandlerThread是繼承于Thread,本質上是一個線程,構造方法需要傳入兩個參數,一個是name指的是線程名稱,一個是priority指的是線程的優先級,當建立了HandlerThread對象并開啟線程start方法後,run方法就會馬上執行,接下來看看run方法:
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
//在目前線程初始化一個Looper對象
Looper.prepare();
synchronized (this) {
//把初始化的Looper對象指派給HandlerThread的内部mLooper
mLooper = Looper.myLooper();
//喚醒等待線程
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -;
}
從源碼可以看出,當run方法被執行後,會調用Looper.prepare()建立一個Looper對象和MessageQueue對象并綁定了在目前線程(異步線程);這樣才可以Looper對象以參數的形式傳給Handler對象,進而確定Handler的hanldMessage方法是在子線程(異步線程)中執行的;接下來會把建立的Looper對象指派給HandlerThread的内部變量mLooper,并通過notifyAll方法喚醒等待線程,最後調用loop方法,中間有個onLooperPrepared方法,可以重寫此方法,也就是說在loop開啟循環之前,可以做一些其他的操作。有個疑問就是在loop開啟循環之前,為什麼要喚醒等待線程呢,接下來看看getLooper方法:
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper .
傳回此線程關聯的Looper對象
*/
public Looper getLooper() {
//如果線程沒有開啟,則傳回null
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
//如果線程被開啟了,而且目前線程Looper為null時,此時會等待狀态,直到mLooper被初始化并指派後會被喚醒
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
當外部主線程調用getLooper方法,先會判斷目前異步線程是否啟動了,如果isAlive()為false ,說明線程未開啟,會傳回null,如果線程啟動了,則會進入同步代碼塊内并判斷mLooper是否為null,也就是内部變量mLooper是否被指派,為null表示未指派尚未初始化,進入等待狀态,直到mLooper被指派後,notifyAll方法會喚醒是以的等待線程 ,最後傳回Looper對象。 之是以有個等待喚醒機制,主要getLooper方法是在主線程調用的,而mLooper是在子線程初始化指派的,也就是我們無法保證不同線程通路同一個對象 是否被建立了,也就是說在調用getLooper方法來擷取Looper對象時,無法確定mLooper已經被建立了 ,,故引入等待喚醒機制來確定線程建立開啟後 并且Looper對象 也被建立後才可以成功傳回mLooper
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
線程的退出提供兩個方法,一個quit,一個quitSafely,quitSafely相對更安全下。
- quit:會清除MessageQueue中全部的消息,無論是延遲還是非延遲的消息都清除。
- quitSafely:該方法隻會清空MessageQueue消息池中所有的延遲消息,并将消息池中所有的非延遲消息派發出去讓Handler去處理完成後才停止Looper循環。