Android中HandlerThread類【源碼分析】

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)。

參考:Android 多線程之HandlerThread 完全詳解
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猜旬,更是在濱河造成了極大的恐慌椿争,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昧狮,死亡現(xiàn)場(chǎng)離奇詭異合住,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門涉馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事讯泣≡暮罚” “怎么了好渠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵昨稼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拳锚,道長(zhǎng)假栓,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任霍掺,我火速辦了婚禮匾荆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抗楔。我一直安慰自己棋凳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布连躏。 她就那樣靜靜地躺著,像睡著了一般贞滨。 火紅的嫁衣襯著肌膚如雪入热。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天晓铆,我揣著相機(jī)與錄音勺良,去河邊找鬼。 笑死骄噪,一個(gè)胖子當(dāng)著我的面吹牛尚困,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播链蕊,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼事甜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了滔韵?” 一聲冷哼從身側(cè)響起逻谦,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陪蜻,沒(méi)想到半個(gè)月后邦马,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宴卖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年滋将,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片症昏。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡随闽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出齿兔,到底是詐尸還是另有隱情橱脸,我是刑警寧澤础米,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站添诉,受9級(jí)特大地震影響屁桑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜栏赴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一蘑斧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧须眷,春花似錦竖瘾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至扩劝,卻和暖如春庸论,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棒呛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工聂示, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人簇秒。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓鱼喉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親趋观。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扛禽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355