HandlerThread 與 IntentService 源碼解析

HandlerThread 是線程類斤葱,主動綁定 Looper 哑蔫,為 Handler 提供了消息隊(duì)列闭树,IntentService 中通過為 Handler 綁定 HandlerThread 提供的子線程 Looper漾峡,從而使 Handler 處理消息在子線程幔烛,由于 Looper 是死循環(huán)导而,所以在 IntentService 銷毀時(shí)辕录,必須停止 Looper 中消息隊(duì)列的循環(huán) MessageQueue.quit() 玫鸟,從而停止 HandlerThread退渗。

HandlerThread 原理解析

繼承了 Thread 類移稳,是一個(gè)線程類,在 run() 方法中 調(diào)用了 Looper.prepare() 和 Looper.loop() 方法会油,所以該線程默認(rèn)已經(jīng)綁定了 Looper

還提供了獲取綁定的 Looper 對象的方法 getLooper(); 調(diào)用該方法時(shí)如果 Looper 沒有初始化則調(diào)用該方法的該線程會 wait() 等待个粱,Looper 對象綁定之后則會 notifyAll(); 喚醒所有 wait() 的線程,從而保證了 getLooper() 方法會得到正確的 Looper 對象钞啸。

獲取到 Looper 之后就可以創(chuàng)建與該子線程綁定的 Handler 几蜻,通過 Handler 就可以發(fā)送消息,從而在子線程中處理消息体斩。

HandlerThread 的 run 方法為一個(gè)死循環(huán)梭稚,終止運(yùn)行 使用 quit 方法和 quitSafely 方法,兩個(gè)方法的區(qū)別是 MessageQueue 消息隊(duì)列中對消息的處理

quit 方法中直接回收每一個(gè)消息絮吵,quitSafely 中會將大于當(dāng)前時(shí)間的未處理消息直接回收弧烤,對已經(jīng)開始執(zhí)行的消息,會安全回收

HandlerThread 存在意義

HandlerThread 作為一個(gè)線程對象蹬敲,其存在意義為暇昂,提供了一個(gè)綁定到子線程的 Looper ,從而得到在子線程中輪詢的消息隊(duì)列伴嗡, 從而可以初始化時(shí) Handler 綁定的消息隊(duì)列為子線程急波。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();  // 喚醒 wait() 的線程
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait(); // 等待 Looper 對象創(chuàng)建
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

IntentService

  1. 繼承了 Service 類,是服務(wù)瘪校。使用時(shí)需要重寫其內(nèi)部的抽象方法 onHandleIntent() ;
  2. 在 onCreat() 方法中創(chuàng)建了 HandlerThread 對象澄暮,通過調(diào)用 HandlerThread 對象的 getLooper() 方法得到了子線程的 Looper 對象;
  3. 接著通過該 Looper 對象創(chuàng)建了內(nèi)部類 ServiceHandler 的對象阱扬,該 ServiceHandler 處理消息時(shí)在 HandlerThread 線程中泣懊。
  4. 接著在 onStart() 方法中,調(diào)用 ServiceHandler 的 obtainMessage() 方法創(chuàng)建了一個(gè) Message 對象麻惶,該方法是 Handler 中的方法馍刮,實(shí)現(xiàn)是通過調(diào)用 Message.obtain(); 方法創(chuàng)建 Message 對象,obtain() 方法需要一個(gè) Handler 對象窃蹋,傳入 ServiceHandler 本身即可卡啰。創(chuàng)建 Message 對象后静稻,使用 ServiceHandler 將消息發(fā)送出去。
  5. ServiceHandler 發(fā)送消息后碎乃,根據(jù)消息處理機(jī)制 ServiceHandler 會收到該消息姊扔,此時(shí)即調(diào)用重寫的 IntentSErvice 的 onHandleIntent() 方法惠奸,在 子線程(ServiceHandler 中的 HandlerThread) 中處理消息梅誓。
  6. 最終實(shí)現(xiàn)了 Service 運(yùn)行在子線程。
  7. 處理完消息后調(diào)用了 stopSelf(int id) 方法佛南,之所以沒有調(diào)用 stopSelf() 方法是因?yàn)楣j绻麤]有參數(shù),會直接停止該 Service嗅回,由于 Service 可能被啟動多次及穗,所以調(diào)用 stopSelf 帶參數(shù)的方法,只有當(dāng)參數(shù) id 同啟動 Service 的次數(shù)相同是 Service 才會停止绵载,因?yàn)橄㈥?duì)列中的消息是依次處理的埂陆,所以最后一個(gè)處理結(jié)束后,調(diào)用 StopSelf 帶參數(shù)的方法會正常停止 Service
  8. 在 IntentService 的 onDestory 方法中執(zhí)行了 mServiceLooper 的 quit 方法娃豹,停止 HandlerTreand 中 Looper 的死循環(huán)焚虱,從而安全的退出 HandlerThread 線程。

ServiceHandler (繼承了 Handler 類懂版,是一個(gè) Handler類鹃栽,其 handleMessage() 方法調(diào)用了 IntentService類 的一個(gè)抽象方法 onHandleIntent() 來處理消息。處理完成之后就會自動結(jié)束)

public abstract class IntentService extends Service {

    /**
     * 內(nèi)部類 ServiceHandler
     */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    
    protected abstract void onHandleIntent(@Nullable Intent intent);
    
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }
}

IntentService 是一個(gè)抽象的服務(wù)類躯畴,使用時(shí)只需要新子類繼承 IntentService 并重寫其抽象方法 onHandleIntent() 調(diào)用類其對象的 start() 方法之后民鼓,則在子線程中執(zhí)行 onHandleIntent() 中的代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蓬抄,一起剝皮案震驚了整個(gè)濱河市丰嘉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嚷缭,老刑警劉巖饮亏,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異峭状,居然都是意外死亡克滴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門优床,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劝赔,“玉大人,你說我怎么就攤上這事胆敞∽琶保” “怎么了杂伟?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仍翰。 經(jīng)常有香客問我赫粥,道長,這世上最難降的妖魔是什么予借? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任越平,我火速辦了婚禮,結(jié)果婚禮上灵迫,老公的妹妹穿的比我還像新娘秦叛。我一直安慰自己,他們只是感情好瀑粥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布挣跋。 她就那樣靜靜地躺著,像睡著了一般狞换。 火紅的嫁衣襯著肌膚如雪避咆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天修噪,我揣著相機(jī)與錄音查库,去河邊找鬼。 笑死割按,一個(gè)胖子當(dāng)著我的面吹牛膨报,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播适荣,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼现柠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弛矛?” 一聲冷哼從身側(cè)響起够吩,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丈氓,沒想到半個(gè)月后周循,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡万俗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年湾笛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闰歪。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嚎研,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出库倘,到底是詐尸還是另有隱情临扮,我是刑警寧澤论矾,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站杆勇,受9級特大地震影響贪壳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚜退,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一闰靴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧关霸,春花似錦传黄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽章姓。三九已至佳遣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凡伊,已是汗流浹背零渐。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留系忙,地道東北人诵盼。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像银还,于是被迫代替她去往敵國和親风宁。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容