第11章(3)---HandlerThread和IntentService

HandlerThread

HandlerThread繼承了Thread答憔,它是一種可以使用Handler的Thread秕狰,它的實(shí)現(xiàn)也很簡(jiǎn)單哥纫,就是在run方法中通過(guò)Looper.prepare()來(lái)創(chuàng)建消息隊(duì)列脖镀,并通過(guò)Looper.loop()來(lái)開(kāi)啟消息循環(huán)麸塞,這樣在實(shí)際的使用中就允許在HandlerThread中創(chuàng)建Handler了。HandlerThread的run方法如下所示:

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

從HandlerThread的實(shí)現(xiàn)來(lái)看椰憋,它和普通的Thread有顯著的不同之處波势。普通Thread主要用于在run方法中執(zhí)行一個(gè)耗時(shí)任務(wù),而HandlerThread在內(nèi)部創(chuàng)建了消息隊(duì)列笆檀,外界需要通過(guò)Handler的消息方式來(lái)通知HandlerThread執(zhí)行一個(gè)具體的任務(wù)忌堂。HandlerThread是一個(gè)很有用的類,它在Android中的一個(gè)具體的使用場(chǎng)景是IntentService酗洒。由于HandlerThread的run方法是一個(gè)無(wú)限循環(huán)士修,因此當(dāng)明確不需要再使用HandlerThread時(shí),可以通過(guò)它的quit或者quitSafely方法來(lái)終止線程的執(zhí)行樱衷,這是一個(gè)良好的編程習(xí)慣棋嘲。

11.2.4 IntentService

IntentService是一種特殊的Service,它繼承了Service并且它是一個(gè)抽象類矩桂,因此必須創(chuàng)建它的子類才能使用IntentService沸移。IntentService可用于執(zhí)行后臺(tái)耗時(shí)的任務(wù),當(dāng)任務(wù)執(zhí)行后它會(huì)自動(dòng)停止侄榴,同時(shí)由于IntentService是服務(wù)的原因雹锣,這導(dǎo)致它的優(yōu)先級(jí)比單純的線程要高很多,所以IntentService比較適合執(zhí)行一些高優(yōu)先級(jí)的后臺(tái)任務(wù)牲蜀,因?yàn)樗鼉?yōu)先級(jí)高不容易被系統(tǒng)殺死笆制。在實(shí)際上,IntentService封裝了HandlerThread和Handler涣达,這一點(diǎn)可以從它的onCreate方法中看出來(lái)在辆。

@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);
}

當(dāng)IntentService被第一次啟動(dòng)時(shí),它的onCreate()方法會(huì)被調(diào)用度苔,onCreate()方法會(huì)創(chuàng)建一個(gè)HandlerThread匆篓,然后使用它的Looper來(lái)構(gòu)造一個(gè)Handler對(duì)象mServiceLooper,這樣通過(guò)mServiceLooper發(fā)送的消息最終都會(huì)在HandlerThread中執(zhí)行寇窑,從這個(gè)角度來(lái)看鸦概,IntentService也可以用于執(zhí)行后臺(tái)任務(wù)。每次啟動(dòng)IntentService甩骏,它的onStartCommand方法就會(huì)調(diào)用一次窗市,IntentService在onStartCommand中處理每個(gè)后臺(tái)任務(wù)的Intent。下面看一下onStartCommand方法是如何處理外界的Intent的饮笛,onStartCommand調(diào)用了onStart咨察,onStart方法的實(shí)現(xiàn)如下所示:

@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

IntentService僅僅是通過(guò)mServiceHandler發(fā)送了一個(gè)消息,這個(gè)消息會(huì)在HandlerThread中被處理福青。mServiceHandler收到消息后摄狱,會(huì)將Intent對(duì)象傳遞給onHandleIntent方法去處理脓诡。注意這個(gè)Intent對(duì)象的內(nèi)容和外界的startService(intent)中的intent的內(nèi)容是完全一致的,通過(guò)這個(gè)Intent對(duì)象即可解析出外界啟動(dòng)IntentService時(shí)所傳遞的參數(shù)媒役,通過(guò)這些參數(shù)就可以區(qū)分具體的后臺(tái)任務(wù)祝谚,這樣在onHandleIntent方法中就可以對(duì)不同的后臺(tái)任務(wù)做處理了。當(dāng)onHandleIntent方法執(zhí)行結(jié)束后酣衷,IntentService會(huì)通過(guò)stopSelf(int startId)方法來(lái)嘗試停止服務(wù)交惯。這里之所以采用stopSelf(int startId)而不是stopSelf()來(lái)停止服務(wù),那是因?yàn)閟topSelf()會(huì)立刻停止服務(wù)穿仪,而這個(gè)時(shí)候可能還有其他消息未處理商玫,stopSelf(int startId)則會(huì)等待所有的消息都處理完畢后才終止服務(wù)。一般來(lái)說(shuō)牡借,stopSelf(int startId)在嘗試停止服務(wù)之前會(huì)判斷最近啟動(dòng)服務(wù)的次數(shù)是否和startId相等拳昌,如果相等就立刻停止服務(wù),不相等則不停止服務(wù)钠龙,這個(gè)策略可以從AMS的stopServiceToken方法的實(shí)現(xiàn)中找到依據(jù)炬藤,感興趣的話可以自行查看源碼實(shí)現(xiàn)。

ServiceHandler的實(shí)現(xiàn)如下所示:

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);
    }
}

IntentService的onHandleIntent方法是一個(gè)抽象方法碴里,它需要我們?cè)谧宇愔袑?shí)現(xiàn)沈矿,它的作用是從Intent參數(shù)中區(qū)分具體的任務(wù)并執(zhí)行這些任務(wù)。如果目前只存在一個(gè)后臺(tái)任務(wù)咬腋,那么onHandleIntent方法執(zhí)行完這個(gè)任務(wù)后羹膳,stopSelf(int startId)就會(huì)直接停止服務(wù),如果目前存在多個(gè)后臺(tái)任務(wù)根竿,那么當(dāng)onHandleIntent方法執(zhí)行完最后一個(gè)任務(wù)時(shí)陵像,stopSelf(int startId)才會(huì)直接停止服務(wù)。另外寇壳,由于每執(zhí)行一個(gè)后臺(tái)任務(wù)就必須啟動(dòng)一次IntentService醒颖,而IntentService內(nèi)部則通過(guò)消息的方式向HandlerThread請(qǐng)求執(zhí)行任務(wù),Handler中的Looper是順序處理消息的壳炎,這就意味著IntentService也是順序執(zhí)行后臺(tái)任務(wù)的泞歉,當(dāng)有多個(gè)后臺(tái)任務(wù)同時(shí)存在時(shí),這些后臺(tái)任務(wù)會(huì)按照外界發(fā)起的順序排隊(duì)執(zhí)行匿辩。

下面通過(guò)一個(gè)示例來(lái)進(jìn)一步說(shuō)明IntentService的工作方式腰耙,首先派生一個(gè)IntentService的子類。

public class LocalIntentService extends IntentService {
     
     private static final String TAG = "LocalIntentService";

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public LocalIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String action = intent.getStringExtra("task_action");
        Log.d(TAG,"receive task :" + action);
        SystemClock.sleep(3000);
        if ("com.ryg.action.TASK1".equals(action)) {
            Log.d(TAG,"handle task:" + action);
        }
    }

    @Override
    public void onDestroy() {
        Log.d(TAG,"service destroyed.");
        super.onDestroy();
    }
}

在onHandleIntent方法中會(huì)從參數(shù)中解析出后臺(tái)任務(wù)的標(biāo)識(shí)铲球,即task_action字段所代表的內(nèi)容挺庞,然后根據(jù)不同的任務(wù)標(biāo)識(shí)來(lái)執(zhí)行具體的后臺(tái)任務(wù)。這里為了簡(jiǎn)單起見(jiàn)睬辐,直接通過(guò)SystemClock.sleep(3000)來(lái)休眠3000毫秒從而模擬一種耗時(shí)的后臺(tái)任務(wù)挠阁,另外為了驗(yàn)證IntentService的停止時(shí)機(jī),這里在onDestroy()中打印了一句日志溯饵。LocalIntentService實(shí)現(xiàn)完成了以后侵俗,就可以在外界請(qǐng)求執(zhí)行后臺(tái)任務(wù)了,在下面的代碼中先后發(fā)起了3個(gè)后臺(tái)任務(wù)的請(qǐng)求:

Intent service = new Intent(this,LocalIntentService.class);
service.putExtra("task_action","com.ryg.action.TASK1");
startService(service);

service.putExtra("task_action","com.ryg.action.TASK2");
startService(service);

service.putExtra("task_action","com.ryg.action.TASK3");
startService(service);

運(yùn)行程序丰刊,觀察日志隘谣,如下所示:

三個(gè)后臺(tái)任務(wù)是排隊(duì)執(zhí)行的,它們的執(zhí)行順序就是它們發(fā)起請(qǐng)求對(duì)的順序啄巧,即TASK1寻歧,TASK2,TASK3秩仆。另外一點(diǎn)就是當(dāng)TASK3執(zhí)行完畢后码泛,LocalIntentService才真正地停止,從日志中可以看出LocalIntentService執(zhí)行了onDestroy()澄耍,這也意味著服務(wù)正在停止噪珊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市齐莲,隨后出現(xiàn)的幾起案子痢站,更是在濱河造成了極大的恐慌,老刑警劉巖选酗,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阵难,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡芒填,警方通過(guò)查閱死者的電腦和手機(jī)呜叫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殿衰,“玉大人怀偷,你說(shuō)我怎么就攤上這事〔ゾ粒” “怎么了椎工?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜀踏。 經(jīng)常有香客問(wèn)我维蒙,道長(zhǎng),這世上最難降的妖魔是什么果覆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任颅痊,我火速辦了婚禮,結(jié)果婚禮上局待,老公的妹妹穿的比我還像新娘斑响。我一直安慰自己菱属,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布舰罚。 她就那樣靜靜地躺著纽门,像睡著了一般。 火紅的嫁衣襯著肌膚如雪营罢。 梳的紋絲不亂的頭發(fā)上赏陵,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音饲漾,去河邊找鬼蝙搔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛考传,可吹牛的內(nèi)容都是我干的吃型。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼僚楞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼败玉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起镜硕,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤运翼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后兴枯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體血淌,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年财剖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悠夯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躺坟,死狀恐怖沦补,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咪橙,我是刑警寧澤夕膀,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站美侦,受9級(jí)特大地震影響产舞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜菠剩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一易猫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧具壮,春花似錦准颓、人聲如沸哈蝇。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炮赦。三九已至,卻和暖如春贯被,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妆艘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工彤灶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人批旺。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓幌陕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親汽煮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子搏熄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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