IntentService源碼解析

為什么我們需要IntentService ?

Android中的IntentService是繼承自Service類的,在我們討論IntentService之前叨叙,我們先想一下Service的特點(diǎn): Service的回調(diào)方法(onCreate喘帚、onStartCommand垫竞、onBind郑原、onDestroy)都是運(yùn)行在主線程中的漓藕。當(dāng)我們通過startService啟動(dòng)Service之后砸喻,我們就需要在Service的onStartCommand方法中寫代碼完成工作柔逼,但是onStartCommand是運(yùn)行在主線程中的,如果我們需要在此處完成一些網(wǎng)絡(luò)請(qǐng)求或IO等耗時(shí)操作割岛,這樣就會(huì)阻塞主線程UI無響應(yīng)愉适,從而出現(xiàn)ANR現(xiàn)象。為了解決這種問題癣漆,最好的辦法就是在onStartCommand中創(chuàng)建一個(gè)新的線程维咸,并把耗時(shí)代碼放到這個(gè)新線程中執(zhí)行。由此看來惠爽,創(chuàng)建一個(gè)帶有工作線程的Service是一種很常見的需求(因?yàn)楣ぷ骶€程不會(huì)阻塞主線程)癌蓖,所以Android為了簡(jiǎn)化開發(fā)帶有工作線程的Service,Android額外開發(fā)了一個(gè)類——–IntentService婚肆。

IntentService的特點(diǎn)

  • 獨(dú)立的線程
    onCreate租副、onStartCommand、onDestroy回調(diào)方法都是運(yùn)行在主線程中的较性,而onHandleIntent是運(yùn)行在工作線程IntentService中的用僧。
  • 自動(dòng)創(chuàng)造一個(gè)隊(duì)列處理事務(wù)
    當(dāng)我們通過startService多次啟動(dòng)了IntentService,這會(huì)產(chǎn)生多個(gè)job赞咙,由于IntentService只持有一個(gè)工作線程责循,所以每次onHandleIntent只能處理一個(gè)job。面多多個(gè)job人弓,IntentService會(huì)如何處理沼死?處理方式是one-by-one,也就是一個(gè)一個(gè)按照先后順序處理崔赌,先將intent1傳入onHandleIntent意蛀,讓其完成job1耸别,然后將intent2傳入onHandleIntent,讓其完成job2…這樣直至所有job完成县钥,所以我們IntentService不能并行的執(zhí)行多個(gè)job秀姐,只能一個(gè)一個(gè)的按照先后順序完成,當(dāng)所有job完成的時(shí)候IntentService就銷毀了若贮,會(huì)執(zhí)行onDestroy回調(diào)方法省有。
  • 當(dāng)所有的請(qǐng)求處理完之后,自動(dòng)銷毀谴麦,不用調(diào)用 stopSelf()
  • 自動(dòng) onBind 返回 null
  • 只要將需要處理的事務(wù)放在onHandleIntent()蠢沿,F(xiàn)ramework會(huì)回調(diào)其onHandleIntent方法。
  • 在service構(gòu)造函數(shù)要返回super(“ServiceName”);

IntentService的使用

MainActivity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent bindIntent = new Intent(this, MyIntentService.class);
        bindIntent.putExtra("name","task1");
        startService(bindIntent);
        bindIntent.putExtra("name","task2");
        startService(bindIntent);
        bindIntent.putExtra("name","task2");
        startService(bindIntent);
    }
}

MyIntentService

public class MyIntentService extends IntentService {
    String Tag = "MyIntentService";

    public MyIntentService() {
        super("MyIntentService");
        Log.i(Tag, "MyIntentService構(gòu)造函數(shù), Thread: " + Thread.currentThread().getName());
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(Tag, "MyIntentService -> onCreate, Thread: " + Thread.currentThread().getName());
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i(Tag, "MyIntentService -> onStartCommand, Thread: " + Thread.currentThread().getName() + " , startId: " + startId);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(Tag, "MyIntentService -> onDestroy, Thread: " + Thread.currentThread().getName());
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String name = intent.getStringExtra("name");
        //執(zhí)行下載任務(wù)匾效,無法新開線程舷蟀;
        Log.i(Tag, "MyIntentService -> onHandleIntent, Thread: " + Thread.currentThread().getName() + ", 《" + name + "》任務(wù)完成");
    }
}

結(jié)果

06-24 03:18:09.819 27760-27760/? I/MyIntentService: MyIntentService構(gòu)造函數(shù), Thread: main
06-24 03:18:09.819 27760-27760/? I/MyIntentService: MyIntentService -> onCreate, Thread: main
06-24 03:18:09.819 27760-27760/? I/MyIntentService: MyIntentService -> onStartCommand, Thread: main , startId: 1
06-24 03:18:09.819 27760-27760/? I/MyIntentService: MyIntentService -> onStartCommand, Thread: main , startId: 2
06-24 03:18:09.819 27760-27760/? I/MyIntentService: MyIntentService -> onStartCommand, Thread: main , startId: 3
06-24 03:18:09.820 27760-27781/? I/MyIntentService: MyIntentService -> onHandleIntent, Thread: IntentService[MyIntentService], 《task1》任務(wù)完成
06-24 03:18:09.820 27760-27781/? I/MyIntentService: MyIntentService -> onHandleIntent, Thread: IntentService[MyIntentService], 《task2》任務(wù)完成
06-24 03:18:09.820 27760-27781/? I/MyIntentService: MyIntentService -> onHandleIntent, Thread: IntentService[MyIntentService], 《task2》任務(wù)完成
06-24 03:18:10.166 27760-27760/? I/MyIntentService: MyIntentService -> onDestroy, Thread: main

通過以上的輸出結(jié)果我們可以發(fā)現(xiàn),MyIntentService的onCreate面哼、onStartCommand野宜、onDestroy回調(diào)方法都是運(yùn)行在主線程main中的,而onHandleIntent是運(yùn)行在工作線程IntentService[MyIntentService]中的魔策,這驗(yàn)證了我們上面所說的IntentService的第一個(gè)和第二個(gè)特點(diǎn)匈子。
通過上面的輸出結(jié)果我們還會(huì)發(fā)現(xiàn),在我們連續(xù)調(diào)用了三次startService(intent)之后闯袒,onStartCommand依次被調(diào)用了三次虎敦,然后依次執(zhí)行了onHandleIntent三次,這樣就依次完成了job搁吓,當(dāng)最后一個(gè)job完成原茅,也就是在最后一次onHandleIntent調(diào)用完成之后,整個(gè)IntentService的工作都完成堕仔,執(zhí)行onDestroy回調(diào)方法擂橘,IntentService銷毀。

IntentService源碼分析

intentservice = HandlerThread + Service

 
public abstract class IntentService extends Service {
 private volatile Looper mServiceLooper;
 private volatile ServiceHandler mServiceHandler;
 private String mName;
 private boolean mRedelivery;
 
 private final class ServiceHandler extends Handler {
  public ServiceHandler(Looper looper) {
   super(looper);
  }
 
  @Override
  public void handleMessage(Message msg) {
   //在工作線程中調(diào)用onHandleIntent摩骨,確保onHandleIntent不會(huì)阻塞主線程
   onHandleIntent((Intent)msg.obj);
   //在執(zhí)行完了onHandleIntent之后通贞,我們需要調(diào)用stopSelf(startId)聲明某個(gè)job完成了
   //當(dāng)所有job完成的時(shí)候,Android就會(huì)回調(diào)onDestroy方法恼五,銷毀IntentService
   stopSelf(msg.arg1);
  }
 }
 
 public IntentService(String name) {
  //此處的name將用作線程名稱
  super();
  mName = name;
 }
 
 public void setIntentRedelivery(boolean enabled) {
  mRedelivery = enabled;
 }
 
 @Override
 public void onCreate() {
  super.onCreate();
  //創(chuàng)建HandlerThread昌罩,利用mName作為線程名稱,HandlerThread是IntentService的工作線程
  HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
  thread.start();
 
  mServiceLooper = thread.getLooper();
  //將創(chuàng)建的HandlerThread所綁定的looper對(duì)象傳遞給ServiceHandler灾馒,
  //這樣我們創(chuàng)建的Handler就和HandlerThread通過消息隊(duì)列綁定在了一起
  mServiceHandler = new ServiceHandler(mServiceLooper);
 }
 
 @Override
 public void onStart(Intent intent, int startId) {
  //在此方法中創(chuàng)建Message對(duì)象茎用,并將intent作為Message的obj參數(shù),
  //這樣Message與Intent就關(guān)聯(lián)起來了
  Message msg = mServiceHandler.obtainMessage();
  msg.arg1 = startId;
  msg.obj = intent;
  //將關(guān)聯(lián)了Intent信息的Message發(fā)送給Handler
  mServiceHandler.sendMessage(msg);
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  //IntentService重寫了onStartCommand回調(diào)方法:在內(nèi)部調(diào)用onStart回調(diào)方法
  //所以我們?cè)诶^承IntentService時(shí),不應(yīng)該再覆寫該方法,即便覆蓋該方法轨功,我們也應(yīng)該調(diào)用super.onStartCommand()
  onStart(intent, startId);
  return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
 }
 
 @Override
 public void onDestroy() {
  //在onDestroy方法中調(diào)用了Handler的quit方法旭斥,該方法會(huì)終止消息循環(huán)
  mServiceLooper.quit();
 }
 
 @Override
 public IBinder onBind(Intent intent) {
  return null;
 }

 protected abstract void onHandleIntent(Intent intent);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市古涧,隨后出現(xiàn)的幾起案子垂券,更是在濱河造成了極大的恐慌,老刑警劉巖羡滑,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菇爪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡柒昏,警方通過查閱死者的電腦和手機(jī)凳宙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昙楚,“玉大人近速,你說我怎么就攤上這事】熬桑” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵奖亚,是天一觀的道長(zhǎng)淳梦。 經(jīng)常有香客問我,道長(zhǎng)昔字,這世上最難降的妖魔是什么爆袍? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮作郭,結(jié)果婚禮上陨囊,老公的妹妹穿的比我還像新娘。我一直安慰自己夹攒,他們只是感情好蜘醋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咏尝,像睡著了一般压语。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上编检,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天胎食,我揣著相機(jī)與錄音,去河邊找鬼允懂。 笑死厕怜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粥航,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼琅捏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了躁锡?” 一聲冷哼從身側(cè)響起午绳,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎映之,沒想到半個(gè)月后拦焚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杠输,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年赎败,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蠢甲。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡僵刮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鹦牛,到底是詐尸還是另有隱情搞糕,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布曼追,位于F島的核電站窍仰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏礼殊。R本人自食惡果不足惜驹吮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晶伦。 院中可真熱鬧碟狞,春花似錦、人聲如沸婚陪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽近忙。三九已至竭业,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間及舍,已是汗流浹背未辆。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锯玛,地道東北人咐柜。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓兼蜈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親拙友。 傳聞我的和親對(duì)象是個(gè)殘疾皇子为狸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 前幾篇文章帶領(lǐng)大家對(duì)HandlerThread的用法及原理進(jìn)行了深入探索,為了鞏固大家對(duì)HandlerThread...
    伐冰閱讀 1,040評(píng)論 0 1
  • 簡(jiǎn)介 首先我們先來了解HandlerThread和IntentService是什么遗契,以及為什么要將這兩者放在一起分...
    jtsky閱讀 715評(píng)論 0 9
  • 概述 IntentService是Service的子類辐棒,通過實(shí)現(xiàn)onHandleIntent(Intent int...
    蕓蕓水中一滴水閱讀 239評(píng)論 0 0
  • 前言:本文所寫的是博主的個(gè)人見解,如有錯(cuò)誤或者不恰當(dāng)之處牍蜂,歡迎私信博主漾根,加以改正!原文鏈接鲫竞,demo鏈接 Serv...
    PassersHowe閱讀 1,405評(píng)論 0 5
  • 每一個(gè)新生產(chǎn)品辐怕,在發(fā)展初期都會(huì)經(jīng)歷一段艱辛的波折,這是事務(wù)發(fā)展的必然規(guī)律从绘。就像城市街角大街小巷擺放的共享單車寄疏,在共...
    sunnywhf閱讀 311評(píng)論 0 0