HandlerThread類
應(yīng)用場(chǎng)景:
Android中特有的催享,在子線程使用Handler的類倒谷,內(nèi)部封裝了Looper操作巷燥,調(diào)用start()方法即可陨献。
具備的特征:
- HandlerThread本質(zhì)上是一個(gè)線程類,它繼承了Thread捡遍;
- HandlerThread有自己的內(nèi)部Looper對(duì)象锌订,可以進(jìn)行l(wèi)ooper循環(huán);
- 通過(guò)獲取HandlerThread的looper對(duì)象傳遞給Handler對(duì)象画株,可以在handleMessage方法中執(zhí)行異步任務(wù)辆飘。
- 創(chuàng)建HandlerThread后必須先調(diào)用HandlerThread.start()方法,Thread會(huì)先調(diào)用run方法谓传,創(chuàng)建Looper對(duì)象蜈项。
【源碼分析】:HandlerThread類
/**
* A {@link Thread} that has a {@link Looper}.
* The {@link Looper} can then be used to create {@link Handler}s.
* <p>
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
public class HandlerThread extends Thread {
int mPriority; //---線程優(yōu)先級(jí)---
int mTid = -1; //---線程id---
Looper mLooper; //---當(dāng)前線程持有的Looper對(duì)象---
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT; //---默認(rèn)線程優(yōu)先級(jí)-不與主線程爭(zhēng)奪---
}
/**
* 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) { //---該方法用于指定線程優(yōu)先級(jí)---
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() { //---空實(shí)現(xiàn)方法---
}
@Override
public void run() { //---run方法,準(zhǔn)備Looper對(duì)象续挟,設(shè)置線程優(yōu)先級(jí)紧卒,讓Looper輪訓(xùn)。---
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //等待喚醒線程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* 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.
* 此方法返回與此線程關(guān)聯(lián)的 Looper诗祸。如果此線程未啟動(dòng)
* 或由于任何原因 isAlive() 返回 false跑芳,此方法將返回 null。如果這個(gè)線程已啟動(dòng)直颅,此方法將阻塞博个,直到循環(huán)器初始化。
* @return 循環(huán)器功偿。
*/
public Looper getLooper() { //---獲取Looper對(duì)象---
if (!isAlive()) { //線程未啟動(dòng)
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) { //加一個(gè)同步鎖
while (isAlive() && mLooper == null) { //當(dāng)線程未啟動(dòng)&&Looper對(duì)象為空
try {
wait(); //線程處于等待狀態(tài)
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
* 返回與此線程關(guān)聯(lián)的共享的Handler對(duì)象
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
* 【譯文:】
* 退出處理程序線程的循環(huán)程序盆佣。
* <p>
* 導(dǎo)致處理程序線程的循環(huán)程序終止而不處理任何
* 消息隊(duì)列中的更多消息。
* </p><p>
* 任何在 Looper 被要求退出后向隊(duì)列發(fā)布消息的嘗試都將失敗械荷。
* 例如共耍,{@link Handler#sendMessage(Message)} 方法將返回 false。
* </p><p class="note">
* 使用此方法可能不安全养葵,因?yàn)槟承┫⒖赡軣o(wú)法傳遞
* 在 Looper 終止之前征堪。考慮使用 {@link #quitSafely} 來(lái)確保
* 所有未完成的工作有序完成关拒。
* </p>
*
* @return 如果 Looper Looper 被要求退出,則為 True庸娱,否則為 false
* 線程尚未開(kāi)始運(yùn)行着绊。
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
* 【譯文:】
* 安全地退出處理線程的looper。
* <p>
* 使處理程序線程的循環(huán)程序在所有剩余消息后立即終止
* 已處理的消息隊(duì)列中已到期的消息熟尉。
* 將不會(huì)發(fā)送具有未來(lái)到期時(shí)間的待處理延遲消息归露。
* </p><p>
* 任何在 Looper 被要求退出后向隊(duì)列發(fā)布消息的嘗試都將失敗。
* 例如斤儿,{@link Handler#sendMessage(Message)} 方法將返回 false剧包。
* </p><p>
* 如果線程尚未啟動(dòng)或已完成(即如果
* {@link #getLooper} 返回 null)恐锦,然后返回 false。
* 否則疆液,looper 會(huì)被要求退出并返回 true一铅。
* </p>
*
* @return 如果 Looper Looper 被要求退出,則為 True堕油,否則為 false
* 線程尚未開(kāi)始運(yùn)行潘飘。
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
* 返回此線程的標(biāo)識(shí)符。請(qǐng)參閱 Process.myTid()掉缺。
*/
public int getThreadId() {
return mTid;
}
}
從源碼可以看出HandlerThread繼續(xù)自Thread,構(gòu)造函數(shù)的傳遞參數(shù)有兩個(gè)卜录,一個(gè)是name指的是線程的名稱,一個(gè)是priority指的是線程優(yōu)先級(jí)眶明,我們根據(jù)需要調(diào)用即可艰毒。其中成員變量mLooper就是HandlerThread自己持有的Looper對(duì)象。onLooperPrepared()該方法是一個(gè)空實(shí)現(xiàn)搜囱,是留給我們必要時(shí)可以去重寫(xiě)的现喳,但是注意重寫(xiě)時(shí)機(jī)是在Looper循環(huán)啟動(dòng)前,再看看run方法:
HandlerThread的常規(guī)使用中分析過(guò)犬辰,在創(chuàng)建HandlerThread對(duì)象后必須調(diào)用其start()方法才能進(jìn)行其他操作嗦篱,而調(diào)用start()方法后相當(dāng)于啟動(dòng)了線程,也就是run方法將會(huì)被調(diào)用幌缝,而我們從run源碼中可以看出其執(zhí)行了Looper.prepare()代碼灸促,這時(shí)Looper對(duì)象將被創(chuàng)建浴栽,當(dāng)Looper對(duì)象被創(chuàng)建后將綁定在當(dāng)前線程(也就是當(dāng)前異步線程)典鸡,這樣我們才可以把Looper對(duì)象賦值給Handler對(duì)象萝玷,進(jìn)而確保Handler對(duì)象中的handleMessage方法是在異步線程執(zhí)行的球碉。接著將執(zhí)行代碼:
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //喚醒等待線程
}
這里在Looper對(duì)象創(chuàng)建后將其賦值給HandlerThread的內(nèi)部變量mLooper睁冬,并通過(guò)notifyAll()方法去喚醒等待線程看疙,最后執(zhí)行Looper.loop();
代碼,開(kāi)啟looper循環(huán)語(yǔ)句脚线。那這里為什么要喚醒等待線程呢拾积?我們來(lái)看看斯碌,getLooper方法肛度。
事實(shí)上可以看出外部在通過(guò)getLooper方法獲取looper對(duì)象時(shí)會(huì)先先判斷當(dāng)前線程是否啟動(dòng)了承耿,如果線程已經(jīng)啟動(dòng)加袋,那么將會(huì)進(jìn)入同步語(yǔ)句并判斷Looper是否為null职烧,為null則代表Looper對(duì)象還沒(méi)有被賦值蚀之,也就是還沒(méi)被創(chuàng)建足删,此時(shí)當(dāng)前調(diào)用線程進(jìn)入等待階段失受,直到Looper對(duì)象被創(chuàng)建并通過(guò) notifyAll()方法喚醒等待線程贱纠,最后才返回Looper對(duì)象,之所以需要等待喚醒機(jī)制辖试,是因?yàn)長(zhǎng)ooper的創(chuàng)建是在子線程中執(zhí)行的,而調(diào)用getLooper方法則是在主線程進(jìn)行的呐馆,這樣我們就無(wú)法保障我們?cè)谡{(diào)用getLooper方法時(shí)Looper已經(jīng)被創(chuàng)建汹来,到這里我們也就明白了在獲取mLooper對(duì)象時(shí)會(huì)存在一個(gè)同步的問(wèn)題收班,只有當(dāng)線程創(chuàng)建成功并且Looper對(duì)象也創(chuàng)建成功之后才能獲得mLooper的值摔桦,HandlerThread內(nèi)部則通過(guò)等待喚醒機(jī)制解決了同步問(wèn)題邻耕。
?從源碼可以看出當(dāng)我們調(diào)用quit方法時(shí)帽衙,其內(nèi)部實(shí)際上是調(diào)用Looper的quit方法而最終執(zhí)行的則是MessageQueue中的removeAllMessagesLocked方法(Handler消息機(jī)制知識(shí)點(diǎn))御滩,該方法主要是把MessageQueue消息池中所有的消息全部清空艾恼,無(wú)論是延遲消息(延遲消息是指通過(guò)sendMessageDelayed或通過(guò)postDelayed等方法發(fā)送)還是非延遲消息钠绍。
??當(dāng)調(diào)用quitSafely方法時(shí)柳爽,其內(nèi)部調(diào)用的是Looper的quitSafely方法而最終執(zhí)行的是MessageQueue中的removeAllFutureMessagesLocked方法磷脯,該方法只會(huì)清空MessageQueue消息池中所有的延遲消息,并將消息池中所有的非延遲消息派發(fā)出去讓Handler去處理完成后才停止Looper循環(huán)俩功,quitSafely相比于quit方法安全的原因在于清空消息之前會(huì)派發(fā)所有的非延遲消息诡蜓。最后需要注意的是Looper的quit方法是基于API 1蔓罚,而Looper的quitSafely方法則是基于API 18的。
注意:
這里的getLooper方法,是指多個(gè)線程使用同一把鎖扣唱,當(dāng)子線程A并沒(méi)有創(chuàng)建Looper對(duì)象時(shí),為了不使getLooper()返回null正歼,因此讓持有的Looper對(duì)象為空的子線程先wait()等待局义,然后子線程B執(zhí)行到run方法時(shí)萄唇,創(chuàng)建了Loopter對(duì)象另萤,此時(shí)執(zhí)行
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //喚醒等待線程
}
notifyAll()這個(gè)方法去喚醒子線程A四敞。
問(wèn)題:為什么子線程A執(zhí)行了HandlerThread.start()方法,使用getLooper方法還是拿不到Looper對(duì)象呢铺厨?
這是因?yàn)榭赡蹵調(diào)用了start()方法后碘梢,沒(méi)能立即進(jìn)行run()方法中的Looper獲取,因?yàn)榫€程創(chuàng)建需要時(shí)間,此時(shí)getLooper會(huì)進(jìn)行wait()
而子線程B調(diào)用了start方法后缕减,同步鎖代碼塊的Looper對(duì)象進(jìn)行了賦值操作,然后喚醒爭(zhēng)奪該資源的所有線程裹芝,此時(shí)子線程A被B喚醒,Looper不為空怜械,子線程也處于Alive狀態(tài),因此不會(huì)再處于wait()狀態(tài)。