IntentService源碼閱讀

IntentService源碼閱讀

作者:jianzi

原文鏈接:IntentService源碼閱讀

更新時間:2016.11.29

Service作為Android四大組件之一,經(jīng)常會被用到,用于處理后臺事務枫攀。雖然Service經(jīng)常用于處理后臺事務,但是Service的生命周期回調函數(shù)都是在主線程中執(zhí)行的冯乘。

筆者在一開始接觸Service的時候,以為Service專門是用來執(zhí)行耗時任務的晒夹,因此在生命周期回調函數(shù)中做了很多耗時的操作裆馒。結果是顯而易見的姊氓,引發(fā)ANR了問題。Service的生命周期函數(shù)都是運行在主線程的喷好,因此耗時操作超過5s(不太確認)后之后就會出現(xiàn)ANR錯誤翔横。

為了解決ANR問題,常用的方式有Service生命周期中啟動一個子線程梗搅,在子線程中進行耗時操作禾唁。另外Android API提供了一個IntentService,來處理比較耗時的后臺任務无切。

IntentService的用法和普通Service相同荡短,需要在AndroidManifest中進行注冊,并通過startService啟動哆键。

但是IntentService多了一個回調函數(shù)onHandleIntent(Intent intent)掘托,集成了IntentService之后,并在這個函數(shù)中進行耗時操作即可籍嘹。IntentService在啟動之后會啟動一個工作線程烫映,而onHandleIntent函數(shù)就運行在這個工作線程中,因此再也不用擔心ANR問題了噩峦。

使用起來和普通Service一樣,調用startService抽兆,即可觸發(fā)onHandleIntent的執(zhí)行识补。

但是使用IntentService還需要注意幾個問題:

  • IntentService需要一個帶有參數(shù)的構造函數(shù),但是Service需要一個不帶參數(shù)的默認構造函數(shù)辫红,因此再重載構造函數(shù)的時候凭涂,需要注意這一點。
  • IntentService只會啟動一個工作線程贴妻,因此所有的onHandleIntent都會在這一個線程中串行的去執(zhí)行切油,同一時間只有一個任務被處理。
  • 如果需要重寫onStartCommand方法名惩,一定要調用父類的實現(xiàn)澎胡。

下面我們來看看IntentService的源碼(加上注釋都沒超過200行代碼),他到底是如何實現(xiàn)異步處理的娩鹉,和普通Service+Thread有什么區(qū)別攻谁。

我們首先看看onCreate函數(shù):

@Override
public void onCreate() {
    super.onCreate();
    //創(chuàng)建一個工作線程
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    //獲取工作線程的Looper
    mServiceLooper = thread.getLooper();
    //創(chuàng)建一個Handler,用于在主線程中和工作線程通信
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

我們看到弯予,首先創(chuàng)建了一個HandlerThread實例戚宦,我們后面會介紹他的實現(xiàn),現(xiàn)在只需要記住他是一個可以使用Handler的Thread就行了锈嫩。創(chuàng)建的這個線程用于處理各種耗時的后臺任務受楼,稱作工作線程垦搬。

同時還創(chuàng)建了一個ServiceHandler實例,我們來看看它的實現(xiàn):

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        //執(zhí)行onHandleIntent艳汽,處理任務
        onHandleIntent((Intent)msg.obj);
        //執(zhí)行完之后猴贰,關閉Service
        stopSelf(msg.arg1);
    }
}

我們可以看到,ServiceHandler其實就是一個Handler的子類骚灸,而onHandleIntent就是在這個地方被調用的糟趾。我們知道Handler使用的哪個線程的Looper,handlerMessage就是在哪個線程執(zhí)行甚牲,因此onHandleIntent確實是在工作線程中執(zhí)行的义郑。而且同時也解釋了為什么所有的任務是串行的,因為只有一個工作線程丈钙,通過Looper來維持一個消息隊列非驮,來串行的執(zhí)行這些任務。

每次處理完任務之后雏赦,都會調用stopSelf來關閉服務劫笙,因此我們不需要在自己調用關閉服務。

現(xiàn)在我們可以猜測星岗,IntentService應該會在onStartCommand中通過ServiceHandler來發(fā)送消息填大,觸發(fā)onHandleIntent的執(zhí)行。我們繼續(xù)看代碼:

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

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

和我們猜測的一樣俏橘,在onStartCommand中調用了onStart方法向工作線程發(fā)送了一條消息允华,并將Intent傳遞過去,從而觸發(fā)了onHandleIntent的執(zhí)行寥掐。

并且注釋中也介紹了靴寂,不建議在子類中重載onStartCommand方法,因為里面有消息的處理召耘。如果必須要重載的話百炬,需要調用父類的實現(xiàn),否則onHandleIntent是不會被觸發(fā)執(zhí)行的污它。

最后我們來看看onDestroy函數(shù):

Override
public void onDestroy() {
    mServiceLooper.quit();
}

在這個函數(shù)中剖踊,使用在onCreate獲取的Looper實例衫贬,關閉整個消息隊列蜜宪。因此工作線程也會結束執(zhí)行,整個Service結束運行祥山。

最后,我們來總結一下:

  • onCreate:啟動工作線程缝呕,并創(chuàng)建和工作線程通信的Handler澳窑;
  • onStartCommand:通過Handler將消息轉發(fā)到工作線程中斧散,觸發(fā)onHandleIntent的執(zhí)行,處理耗時任務摊聋;
  • onHandleIntent:調用者自己需要執(zhí)行的耗時的后臺任務鸡捐;
  • onDestroy:關閉工作線程的消息隊列,退出工作線程麻裁,結束Service箍镜。

看完源碼之后,我們會發(fā)現(xiàn)使用IntentService和Service+Thread沒有本質上的區(qū)別煎源。但是個人還是建議色迂,如果有耗時的任務優(yōu)先使用IntentService進行處理。

因為IntentService內部只維持了一個工作線程手销,通過Handler來與其通信歇僧。比起一般的做法,處理一個任務創(chuàng)建就創(chuàng)建一個線程有消耗更小一點锋拖。而且IntentService畢竟是官方推薦的诈悍,接口使用起來十分簡單,不需要開發(fā)者自己去維護線程兽埃,節(jié)省了開發(fā)成本侥钳。

但是也并不是絕對,因為IntentService只有一個工作線程柄错,對于那種需要并發(fā)的任務來講舷夺,還是需要自己處理了。

好了鄙陡,就到這吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末躏啰,一起剝皮案震驚了整個濱河市趁矾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌给僵,老刑警劉巖毫捣,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異帝际,居然都是意外死亡蔓同,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門蹲诀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斑粱,“玉大人,你說我怎么就攤上這事脯爪≡虮保” “怎么了矿微?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尚揣。 經(jīng)常有香客問我涌矢,道長,這世上最難降的妖魔是什么快骗? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任娜庇,我火速辦了婚禮,結果婚禮上方篮,老公的妹妹穿的比我還像新娘名秀。我一直安慰自己,他們只是感情好恭取,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布泰偿。 她就那樣靜靜地躺著,像睡著了一般蜈垮。 火紅的嫁衣襯著肌膚如雪耗跛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天攒发,我揣著相機與錄音调塌,去河邊找鬼。 笑死惠猿,一個胖子當著我的面吹牛羔砾,可吹牛的內容都是我干的。 我是一名探鬼主播偶妖,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼姜凄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趾访?” 一聲冷哼從身側響起态秧,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扼鞋,沒想到半個月后申鱼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡云头,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年捐友,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溃槐。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡匣砖,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情脆粥,我是刑警寧澤砌溺,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站变隔,受9級特大地震影響规伐,放射性物質發(fā)生泄漏。R本人自食惡果不足惜匣缘,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一猖闪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肌厨,春花似錦培慌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至表鳍,卻和暖如春馅而,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背譬圣。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工瓮恭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厘熟。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓屯蹦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绳姨。 傳聞我的和親對象是個殘疾皇子登澜,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容