Android中startService基本使用方法概述
Android中有兩種主要方式使用Service,通過(guò)調(diào)用Context的startService方法或調(diào)用Context的bindService方法,本文只探討純startService的使用,不涉及任何bindService方法調(diào)用的情況。如果想了解bindService的相關(guān)使用,請(qǐng)參見(jiàn)《Android中bindService基本使用方法概述》。
當(dāng)我們通過(guò)調(diào)用了Context的startService方法后,我們便啟動(dòng)了Service,通過(guò)startService方法啟動(dòng)的Service會(huì)一直無(wú)限期地運(yùn)行下去,只有在外部調(diào)用Context的stopService或Service內(nèi)部調(diào)用Service的stopSelf方法時(shí),該Service才會(huì)停止運(yùn)行并銷毀。
要想使用Service,首先我們要繼承自Service,然后重寫如下方法:
onCreate, onStartCommand, onBind 和 onDestroy。
這幾個(gè)方法都是回調(diào)方法,都是由Android操作系統(tǒng)在合適的時(shí)機(jī)調(diào)用的,并且需要注意的是這幾個(gè)回調(diào)方法都是在主線程中被調(diào)用的。
onCreate: 執(zhí)行startService方法時(shí),如果Service沒(méi)有運(yùn)行的時(shí)候會(huì)創(chuàng)建該Service并執(zhí)行Service的onCreate回調(diào)方法;如果Service已經(jīng)處于運(yùn)行中,那么執(zhí)行startService方法不會(huì)執(zhí)行Service的onCreate方法。也就是說(shuō)如果多次執(zhí)行了Context的startService方法啟動(dòng)Service,Service方法的onCreate方法只會(huì)在第一次創(chuàng)建Service的時(shí)候調(diào)用一次,以后均不會(huì)再次調(diào)用。我們可以在onCreate方法中完成一些Service初始化相關(guān)的操作。
onStartCommand: 在執(zhí)行了startService方法之后,有可能會(huì)調(diào)用Service的onCreate方法,在這之后一定會(huì)執(zhí)行Service的onStartCommand回調(diào)方法。也就是說(shuō),如果多次執(zhí)行了Context的startService方法,那么Service的onStartCommand方法也會(huì)相應(yīng)的多次調(diào)用。onStartCommand方法很重要,我們?cè)谠摲椒ㄖ懈鶕?jù)傳入的Intent參數(shù)進(jìn)行實(shí)際的操作,比如會(huì)在此處創(chuàng)建一個(gè)線程用于下載數(shù)據(jù)或播放音樂(lè)等。
onBind: Service中的onBind方法是抽象方法,所以Service類本身就是抽象類,也就是onBind方法是必須重寫的,即使我們用不到。在通過(guò)startService使用Service時(shí),我們?cè)谥貙憃nBind方法時(shí),只需要將其返回null即可。onBind方法主要是用于給bindService方法調(diào)用Service時(shí)才會(huì)使用到。
onDestroy: 通過(guò)startService方法啟動(dòng)的Service會(huì)無(wú)限期運(yùn)行,只有當(dāng)調(diào)用了Context的stopService或在Service內(nèi)部調(diào)用stopSelf方法時(shí),Service才會(huì)停止運(yùn)行并銷毀,在銷毀的時(shí)候會(huì)執(zhí)行Service回調(diào)函數(shù)。
我們?yōu)榱颂骄客ㄟ^(guò)startService方法啟動(dòng)的Service的生命周期以驗(yàn)證上面對(duì)各個(gè)回調(diào)函數(shù)方法的描述,寫了如下的一個(gè)測(cè)試案例。
首先創(chuàng)建一個(gè)服務(wù)類TestService,該類繼承自Service,代碼如下:
package com.ispring.startservicedemo; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class TestService extends Service { @Override public void onCreate() { Log.i("DemoLog","TestService -> onCreate, Thread ID: " + Thread.currentThread().getId()); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread ID: " + Thread.currentThread().getId()); return START_STICKY; } @Override public IBinder onBind(Intent intent) { Log.i("DemoLog", "TestService -> onBind, Thread ID: " + Thread.currentThread().getId()); return null; } @Override public void onDestroy() { Log.i("DemoLog", "TestService -> onDestroy, Thread ID: " + Thread.currentThread().getId()); super.onDestroy(); } }
我們?cè)赥estService的各個(gè)回調(diào)方法中只是簡(jiǎn)單打印出了相應(yīng)的信息,并沒(méi)有做很多復(fù)雜的處理操作。
然后我們?cè)贏ctivity中調(diào)用該Serivce,Activity中相應(yīng)的代碼如下:
package com.ispring.startservicedemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("DemoLog", "Thread ID: " + Thread.currentThread().getId()); Log.i("DemoLog", "before test startService"); //連續(xù)啟動(dòng)Service Intent intent1 = new Intent(this, TestService.class); startService(intent1); Intent intent2 = new Intent(this, TestService.class); startService(intent2); Intent intent3 = new Intent(this, TestService.class); startService(intent3); //停止Service Intent intent4 = new Intent(this, TestService.class); stopService(intent4); //再次啟動(dòng)Service Intent intent5 = new Intent(this, TestService.class); startService(intent5); Log.i("DemoLog", "after test startService"); } }
我們?cè)贏ctivity中,首先連續(xù)三次調(diào)用了Activity的startService方法以啟動(dòng)Service,然后調(diào)用Activity的stopService方法停止Service,然后又通過(guò)調(diào)用Activity的startService方法啟動(dòng)Service。
運(yùn)行程序的輸出結(jié)果如下:
我們分析一下上面的輸出結(jié)果,首先打印出了主線程的ID是1, 然后我們發(fā)現(xiàn)后面所有在回調(diào)函數(shù)中打印出的執(zhí)行線程的ID也就是1,這就說(shuō)明了Service中的各個(gè)回調(diào)方法是運(yùn)行在主線程中的。其次我們可以發(fā)現(xiàn)在我們連續(xù)調(diào)用了三次startService方法之后,只觸發(fā)了一次onCreate回調(diào)方法,觸發(fā)了三次onStartCommand方法,在onStartCommand中我們可以讀取到通過(guò)startService方法傳入的Intent對(duì)象,并且這三次的startId都不同,分別是1,2,3,每次調(diào)用startService都會(huì)自動(dòng)分配一個(gè)startId,startId可以用來(lái)區(qū)分不同的startService的調(diào)用,一般情況下startId都是從1開(kāi)始計(jì)數(shù),以后每次調(diào)用startService之后startId自動(dòng)加一遞增。
之后我們又調(diào)用了Activity的stopService(intent4)方法用于停止Service,通過(guò)輸出結(jié)果我們發(fā)現(xiàn)Service執(zhí)行了onDestroy方法,一般情況下我們可以在onDestroy方法中執(zhí)行一些資源釋放的操作。執(zhí)行完onDestroy之后該Service的實(shí)例就銷毀了。雖然我們之前調(diào)用了三次startService方法,但是只要調(diào)用一次stopService就可以讓運(yùn)行中的Service停止運(yùn)行并銷毀。
最后我們?cè)俅瓮ㄟ^(guò)startService(intent5)啟動(dòng)Service時(shí),通過(guò)輸出結(jié)果我們發(fā)現(xiàn)再次執(zhí)行了Service的onCreate方法,這說(shuō)明Service在通過(guò)stopService銷毀之后重新創(chuàng)建了,并隨之再次調(diào)用onStartCommand回調(diào)方法,并且startId再次從1開(kāi)始計(jì)數(shù)。
最后需要注意的是我們?cè)贏ctivity中操作Service的開(kāi)始和結(jié)尾處分別寫了兩句輸出代碼,分別是
和
但是我們?cè)倏匆幌螺敵鼋Y(jié)果會(huì)發(fā)現(xiàn),程序直接上來(lái)在輸出了before test startService之后,卻立即輸出了after test startService,在這之后才是TestService內(nèi)部各個(gè)回調(diào)方法的輸出,這說(shuō)明startService()方法和stopService()方法在執(zhí)行完后立即返回了,也就是這兩個(gè)方法都不是阻塞式的,啟動(dòng)service和停止service都是異步操作,startService()、stopService()都是將intent對(duì)象發(fā)送給Android Framework,然后Framework層異步地啟動(dòng)、停止Service。
我們用一張圖來(lái)概括一下通過(guò)startService啟動(dòng)的Service的生命周期:
當(dāng)Android面臨內(nèi)存匱乏的時(shí)候,可能會(huì)銷毀掉你當(dāng)前運(yùn)行的Service,然后待內(nèi)存充足的時(shí)候可以重新創(chuàng)建Service,Service被Android系統(tǒng)強(qiáng)制銷毀并再次重建的行為依賴于Service中onStartCommand方法的返回值。我們常用的返回值有三種值,START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT,這三個(gè)值都是Service中的靜態(tài)常量。
START_NOT_STICKY: 如果返回START_NOT_STICKY,表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,不會(huì)重新創(chuàng)建該Service,當(dāng)然如果在其被殺掉之后一段時(shí)間又調(diào)用了startService,那么該Service又將被實(shí)例化。那什么情境下返回該值比較恰當(dāng)呢?如果我們某個(gè)Service執(zhí)行的工作被中斷幾次無(wú)關(guān)緊要或者對(duì)Android內(nèi)存緊張的情況下需要被殺掉且不會(huì)立即重新創(chuàng)建這種行為也可接受,那么我們便可將 onStartCommand的返回值設(shè)置為START_NOT_STICKY。舉個(gè)例子,某個(gè)Service需要定時(shí)從服務(wù)器獲取最新數(shù)據(jù):通過(guò)一個(gè)定時(shí)器每隔指定的N分鐘讓定時(shí)器啟動(dòng)Service去獲取服務(wù)端的最新數(shù)據(jù)。當(dāng)執(zhí)行到Service的onStartCommand時(shí),在該方法內(nèi)再規(guī)劃一個(gè)N分鐘后的定時(shí)器用于再次啟動(dòng)該Service并開(kāi)辟一個(gè)新的線程去執(zhí)行網(wǎng)絡(luò)操作。假設(shè)Service在從服務(wù)器獲取最新數(shù)據(jù)的過(guò)程中被Android系統(tǒng)強(qiáng)制殺掉,Service不會(huì)再重新創(chuàng)建,這也沒(méi)關(guān)系,因?yàn)樵龠^(guò)N分鐘定時(shí)器就會(huì)再次啟動(dòng)該Service并重新獲取數(shù)據(jù)。
START_STICKY: 如果返回START_STICKY,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,Android系統(tǒng)會(huì)將該Service依然設(shè)置為started狀態(tài)(即運(yùn)行狀態(tài)),但是不再保存onStartCommand方法傳入的intent對(duì)象,然后Android系統(tǒng)會(huì)嘗試再次重新創(chuàng)建該Service,并執(zhí)行onStartCommand回調(diào)方法,但是onStartCommand回調(diào)方法的Intent參數(shù)為null,也就是onStartCommand方法雖然會(huì)執(zhí)行但是獲取不到intent信息。如果你的Service可以在任意時(shí)刻運(yùn)行或結(jié)束都沒(méi)什么問(wèn)題,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一個(gè)用來(lái)播放背景音樂(lè)功能的Service就適合返回該值。
START_REDELIVER_INTENT: 如果返回START_REDELIVER_INTENT,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,與返回START_STICKY的情況類似,Android系統(tǒng)會(huì)將再次重新創(chuàng)建該Service,并執(zhí)行onStartCommand回調(diào)方法,但是不同的是,Android系統(tǒng)會(huì)再次將Service在被殺掉之前最后一次傳入onStartCommand方法中的Intent再次保留下來(lái)并再次傳入到重新創(chuàng)建后的Service的onStartCommand方法中,這樣我們就能讀取到intent參數(shù)。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我們的Service需要依賴具體的Intent才能運(yùn)行(需要從Intent中讀取相關(guān)數(shù)據(jù)信息等),并且在強(qiáng)制銷毀后有必要重新創(chuàng)建運(yùn)行,那么這樣的Service就適合返回START_REDELIVER_INTENT。
以上就是關(guān)于startService的基本使用概述及其生命周期介紹,希望對(duì)大家的學(xué)習(xí)有所幫助
相關(guān)文章
Android中使用自定義ViewGroup的總結(jié)
本篇文章主要介紹了Android中使用自定義ViewGroup的總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01SurfaceView開(kāi)發(fā)[捉小豬]手機(jī)游戲 (一)
這篇文章主要介紹了用SurfaceView開(kāi)發(fā)[捉小豬]手機(jī)游戲 (一)本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Android中使用AsyncTask實(shí)現(xiàn)下載文件動(dòng)態(tài)更新進(jìn)度條功能
這篇文章主要介紹了AsyncTask用法解析-下載文件動(dòng)態(tài)更新進(jìn)度條,需要的朋友可以參考下2017-08-08Android開(kāi)發(fā)之設(shè)置開(kāi)機(jī)自動(dòng)啟動(dòng)的幾種方法
這篇文章主要介紹了Android開(kāi)發(fā)之設(shè)置開(kāi)機(jī)自動(dòng)啟動(dòng)的幾種方法的相關(guān)資料,這里提供三種方法幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08Android SeekBar在刷新使用中需要注意的問(wèn)題
SeekBar在刷新使用中需要注意的問(wèn)題:在使用SeekBar的過(guò)程中需要注意刷新頻率,避免頻繁刷新造成的性能問(wèn)題;同時(shí),需要對(duì)SeekBar的監(jiān)聽(tīng)事件進(jìn)行適當(dāng)?shù)膬?yōu)化,減少回調(diào)次數(shù),提高響應(yīng)速度2023-05-05初識(shí)Android?PowerManagerService省電模式
這篇文章主要介紹了初識(shí)Android?PowerManagerService省電模式,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-08-08Flutter實(shí)現(xiàn)心動(dòng)的動(dòng)畫特效
為了追求更好的用戶體驗(yàn),有時(shí)候我們需要一個(gè)類似心跳一樣跳動(dòng)著的控件來(lái)吸引用戶的注意力。本文將利用Flutter實(shí)現(xiàn)這一動(dòng)畫特效,需要的可以參考一下2022-04-04Android沉浸式狀態(tài)欄實(shí)現(xiàn)示例
本篇文章主要介紹了Android沉浸式狀態(tài)欄實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02