IntentService示例代碼和源碼解析

IntentService

在實(shí)際開發(fā)過程中會(huì)有這樣的一個(gè)需求钠惩,我們需要運(yùn)行一個(gè)任務(wù),并且只需要在后臺(tái)默默運(yùn)行即可,那么這樣的需求瓶颠,我們一般會(huì)在 activity 中去開啟一個(gè)線程涩笤,讓其去跑這個(gè)任務(wù),但是 activity 在被切換到后臺(tái)并且因?yàn)閮?nèi)存不足時(shí)會(huì)被回收舅逸。不過還有一種方式可以解決那就是使用 Service 的方式實(shí)現(xiàn),在 Service 中開啟子線程執(zhí)行耗時(shí)操作刨啸,并且 service 的優(yōu)先級(jí)高, 不易被回收识脆。不過 google 為了方便開發(fā)者使用设联,提供了一個(gè) IntentService 這個(gè)類。下面是 google 對(duì)這個(gè)類的描述:


/**
 * IntentService is a base class for {@link Service}s that handle asynchronous
 * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 *
 * <p>This "work queue processor" pattern is commonly used to offload tasks
 * from an application's main thread.  The IntentService class exists to
 * simplify this pattern and take care of the mechanics.  To use it, extend
 * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 * will receive the Intents, launch a worker thread, and stop the service as
 * appropriate.
 *
 * <p>All requests are handled on a single worker thread -- they may take as
 * long as necessary (and will not block the application's main loop), but
 * only one request will be processed at a time.
 */

簡單理解:IntentService是繼承Service異步請(qǐng)求任務(wù)類灼捂,內(nèi)部維護(hù)了HandlerThread和Handler离例,可用于執(zhí)行后臺(tái)耗時(shí)任務(wù)。所有的請(qǐng)求都只會(huì)在一個(gè)單一的線程去執(zhí)行悉稠,并且一次只會(huì)去執(zhí)行一個(gè)請(qǐng)求宫蛆,隊(duì)列的方式順序執(zhí)行所有的任務(wù)完成之后會(huì)自己去停止服務(wù)。正在執(zhí)行的任務(wù)是無法被打斷的的猛。使用IntentService 的好處就是不需要去手動(dòng)的關(guān)閉服務(wù)耀盗,也免去了開啟線程的工作,使用起來很方便

案例

  • 自定義一個(gè)IntentService的子類MyIntentService

    IntentService 是一個(gè)抽象類卦尊,需要子類實(shí)現(xiàn)叛拷,并重寫 onHandleIntent 方法,它是一個(gè)處理接收到服務(wù)的回調(diào)方法岂却。

public class MyIntentService extends IntentService {
    public static final String TAG = MyIntentService.class.getSimpleName();

    public MyIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
       
        if (intent != null) {
            String task = intent.getStringExtra("task");
            Log.e(TAG,"onHandleIntent:"+task);

            if("open".equals(task)) {
                //表示要處理的意圖
                SystemClock.sleep(3000);
                Log.e(TAG,"要特殊處理的意圖:"+task);
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "MyIntentService 被銷毀了");
    }
}

  • 在清單文件配置忿薇,代碼不貼了裙椭。
  • 開啟服務(wù)
Intent service = new Intent(this, MyIntentService.class);

service.putExtra("task", "close1");
startService(service);

service.putExtra("task", "close2");
startService(service);

service.putExtra("task", "open");
startService(service);
  • 結(jié)果

從運(yùn)行結(jié)果可以看出IntentService是逐個(gè)去執(zhí)行任務(wù)的,只有最后一個(gè)任務(wù)執(zhí)行完畢之后才會(huì)去停止服務(wù)

onHandleIntent:close1
onHandleIntent:close2
onHandleIntent:open
要特殊處理的意圖:open
MyIntentService 被銷毀了

源碼分析

  • IntentService#onCreate()

    服務(wù)第一次被創(chuàng)建就會(huì)執(zhí)行 onCreate() 方法署浩,其主要分為兩步:第一步在該方法中創(chuàng)建線程HandlerThread揉燃,thread.start(); 并開啟線程,查閱 HandlerThread#run() 方法源碼可以知道筋栋,HandlerThread 綁定的 Looper 會(huì)調(diào)用 Looper.loop()開啟輪訓(xùn)了炊汤,等待消息的到來。
    第二步是創(chuàng)建 ServiceHandler二汛,并將該 ServiceHandler 綁定HandlerThread創(chuàng)建的Looper對(duì)象婿崭,這樣通過mServiceHandler發(fā)送的消息會(huì)被發(fā)送到HandlerThread中的MessageQueue中。這兩步就是在 IntentService 創(chuàng)建時(shí)的執(zhí)行操作肴颊。

@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();//開啟線程

    mServiceLooper = thread.getLooper();//拿到thread 綁定的 Looper 對(duì)象
    mServiceHandler = new ServiceHandler(mServiceLooper); //根據(jù) Looper 創(chuàng)建 ServiceHandler 對(duì)象氓栈。
}
  • 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;
}
  • IntentService#onStart()

    服務(wù)創(chuàng)建之后就會(huì)去執(zhí)行onStartCommand()方法,該方法會(huì)去調(diào)用onStart()方法婿着。該方法接收到意圖之后授瘦,將其包裝成一個(gè)Message對(duì)象,然后發(fā)送到HandlerThread 對(duì)應(yīng)的消息隊(duì)列中竟宋,這樣 ServiceHandler 將意圖傳遞給onHandleIntent提完。瀏覽源碼發(fā)現(xiàn)該方法是一個(gè)空方法,而且是需要子類去實(shí)現(xiàn)的丘侠。因?yàn)槭窃诜?UI 線程去發(fā)送的 Message徒欣,因此該方法 handleIntent() 可以做一些耗時(shí)任務(wù)。
    注意:onHandleInten方法的Intent參數(shù)跟startService的Intent參數(shù)是同一個(gè)對(duì)象蜗字。

@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}
  • mServiceHandler 是如何處理消息的打肝?

    之前在 onStart 方法是通過 mServiceHandler.sendMessage 的方式發(fā)送消息的,因此會(huì)在該 Handler 中的 handleMessage 中去處理消息挪捕。在該方法中可以看到它將該 Message 中攜帶的 Intent 傳入給 onHandleIntent 方法粗梭。

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);
    }
}
  • Looper 輪訓(xùn)器已經(jīng)開啟輪訓(xùn)了,那么輪訓(xùn)到的 Message 是怎么通過 IntentService#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);//傳遞當(dāng)前的id作為當(dāng)前啟動(dòng)的任務(wù)停止服務(wù)的標(biāo)記断医,不要去調(diào)用stopSelf()方法,這樣會(huì)馬上停止服務(wù)奏纪,有可能其他開啟的服務(wù)還沒執(zhí)行完畢鉴嗤。
    }
}

ServiceHandler是一個(gè)Handler,從onCreate方法可以看出序调,ServiceHandler是綁定HandlerThread的Looper躬窜,也就是說它處于非UI線程,而是跟 HandlerThread 處于同一個(gè)線程炕置。當(dāng)輪訓(xùn)到 Messgae 時(shí)荣挨,會(huì)回調(diào) handleMessage 方法男韧,然后調(diào)用 onHandleIntent 將 msg.obj 作為參數(shù)傳遞過去。這個(gè) msg.obj 就是通過 startService 傳入的 Intent 對(duì)象默垄。

  • 多次啟動(dòng)服務(wù)是如何按順序去執(zhí)行服務(wù)的此虑?

onCreate 方法只會(huì)在第一次開啟時(shí)調(diào)用,多次開啟服務(wù)是不會(huì)再次調(diào)用 onCreate 方法的口锭,而是會(huì)去調(diào)用 onStart 方法朦前,因此 IntentService 中的 HandlerThread 是同一個(gè),也就是所有的任務(wù)都會(huì)一個(gè)線程中去執(zhí)行鹃操。并且會(huì)共用一個(gè) ServiceHandler 對(duì)象韭寸。每次開啟任務(wù),都會(huì)回調(diào) onStart 方法荆隘,然后往消息隊(duì)列中添加一個(gè)任務(wù)恩伺,但是你注意到在 handleMessage 中處理 完 onHandleIntent 之后會(huì)調(diào)用 stopSelf(msg.arg1) 這是不是意味著服務(wù)就被關(guān)閉了呢?其實(shí)不是椰拒,為什么使用 stopSelf(msg.arg1) 可以參考下面一點(diǎn)晶渠。

  • onHandleIntent 執(zhí)行完畢之后為什么是調(diào)用 stopSelf(msg.arg1)而不是 stopSelf() 呢?

    當(dāng)onHandleIntent方法執(zhí)行結(jié)束之后燃观,IntentService會(huì)通過stopSelf(int startId)方法嘗試停止服務(wù)褒脯。這里之所以采用stopSelf(int startId)而不是stopSelf()來停止服務(wù),那是因?yàn)閟topSelf()會(huì)立刻停止服務(wù)缆毁,而這個(gè)時(shí)候可能還有其他消息未處理番川,stopSelf(int startId)則會(huì)等待所有的消息都處理完畢之后才終止服務(wù)。一般來說脊框,stopSelf(int startId)在嘗試停止服務(wù)之前會(huì)判斷最近啟動(dòng)服務(wù)的次數(shù)是否和startId相等颁督,如果相等就立刻停止服務(wù),不相等則不停止服務(wù)缚陷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末适篙,一起剝皮案震驚了整個(gè)濱河市往核,隨后出現(xiàn)的幾起案子箫爷,更是在濱河造成了極大的恐慌,老刑警劉巖聂儒,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虎锚,死亡現(xiàn)場離奇詭異,居然都是意外死亡衩婚,警方通過查閱死者的電腦和手機(jī)窜护,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來非春,“玉大人柱徙,你說我怎么就攤上這事缓屠。” “怎么了护侮?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵敌完,是天一觀的道長。 經(jīng)常有香客問我羊初,道長滨溉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任长赞,我火速辦了婚禮晦攒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘得哆。我一直安慰自己脯颜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布柳恐。 她就那樣靜靜地躺著伐脖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乐设。 梳的紋絲不亂的頭發(fā)上讼庇,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音近尚,去河邊找鬼蠕啄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛戈锻,可吹牛的內(nèi)容都是我干的歼跟。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼格遭,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼哈街!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拒迅,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤骚秦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后璧微,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體作箍,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年前硫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胞得。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屹电,死狀恐怖阶剑,靈堂內(nèi)的尸體忽然破棺而出跃巡,到底是詐尸還是另有隱情,我是刑警寧澤牧愁,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布瓷炮,位于F島的核電站,受9級(jí)特大地震影響递宅,放射性物質(zhì)發(fā)生泄漏娘香。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一办龄、第九天 我趴在偏房一處隱蔽的房頂上張望烘绽。 院中可真熱鬧,春花似錦俐填、人聲如沸安接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盏檐。三九已至,卻和暖如春驶悟,著一層夾襖步出監(jiān)牢的瞬間胡野,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工痕鳍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硫豆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓笼呆,卻偏偏與公主長得像熊响,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诗赌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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

  • 簡介 首先我們先來了解HandlerThread和IntentService是什么汗茄,以及為什么要將這兩者放在一起分...
    jtsky閱讀 717評(píng)論 0 9
  • 從用途上來說,線程分為主線程和子線程铭若,主線程主要處理和界面相關(guān)的事情洪碳,子線程則往往用于執(zhí)行耗時(shí)操作。 除了Thre...
    小柏不是大白閱讀 627評(píng)論 0 3
  • 前幾篇文章帶領(lǐng)大家對(duì)HandlerThread的用法及原理進(jìn)行了深入探索奥喻,為了鞏固大家對(duì)HandlerThread...
    伐冰閱讀 1,046評(píng)論 0 1
  • Android中的線程 線程偶宫,在Android中是非常重要的非迹,主線程處理UI界面环鲤,子線程處理耗時(shí)操作。如果在主線程...
    shenhuniurou閱讀 753評(píng)論 0 3
  • 《參與感》小米手機(jī)背后營銷圣經(jīng) 1:搶首發(fā)憎兽,做頭條冷离。 做產(chǎn)品吵冒,噱頭成不了賣點(diǎn); 做市場西剥,段子成不了頭條. 2:社會(huì)...
    彭先生的雜貨鋪閱讀 688評(píng)論 0 1