一、Service概述
Service是Android中實現(xiàn)程序后臺運行的解決方案,非常適合用于去執(zhí)行那些不需要和用戶交互但還需要長期運行的任務(wù)。
Service不能運行在一個獨立的進(jìn)程當(dāng)中,而是依賴與創(chuàng)建服務(wù)時所在的應(yīng)用程序進(jìn)程。并且只能在后臺運行,并且可以和其他組件進(jìn)行交互。
Service不會自動開啟一個線程,需要我們在Service中手動創(chuàng)建子線程,并在這里執(zhí)行具體的任務(wù)。
二、如何定義Service
步驟如下:
- 創(chuàng)建一個類,繼承android.app.service類。實現(xiàn)抽象方法onBind(),重寫onCreate()、onStartCommand()、onDestroy()。
- 在清單文件AndroidManifest.xml中注冊Service。
1、新建一個MyService類,繼承自Service
public class MyService extends Service {
public static final String TAG = "MyService";
//創(chuàng)建服務(wù)時調(diào)用
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
//服務(wù)執(zhí)行的操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
//銷毀服務(wù)時調(diào)用
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
2、在清單文件中注冊
<service android:name=".MyService"> </service>
Service可以有兩種啟動方式:一種是startService(),另一種是bindService(),下面分別進(jìn)行說明。
三、啟動和停止Service
分別構(gòu)建出兩個Intent對象,用來調(diào)用startService()方法和stopService()方法,來啟動和停止MyService。
注意:startService()和stopService()方法都是定義在Context類當(dāng)中的,所以可以在Activity中直接調(diào)用這兩個方法。
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1_start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.button2_stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
1、啟動服務(wù)
點擊button1_start_service按鈕,啟動服務(wù),后臺打印日志如下:

再連續(xù)點三次button1_start_service按鈕,后臺增加的日志如下:

結(jié)論:onCreate()方法只會在Service第一次被創(chuàng)建的時候調(diào)用,而onStartCommand()方法在每次啟動服務(wù)的時候都會調(diào)用。
注意:
- 服務(wù)對象同時只會有一個。
- 默認(rèn)情況下,一個started的Service與啟動他的組件在同一個線程中。上面的實例中,服務(wù)就是在主線程中運行的,如果是在服務(wù)中完成耗時操作的話,容易造成主線程阻塞。
2、停止服務(wù)
停止一個started服務(wù)有兩種方法:
(1)在外部使用stopService()。
(2)在服務(wù)內(nèi)部(onStartCommand方法內(nèi)部)使用stopSelf()方法。
3、onStartCommand方法的返回值:
onStartCommand方法執(zhí)行時,返回的是一個int型。這個整型可以有三個返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT。
(1)START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執(zhí)行完onStartCommand方法后,服務(wù)被異常kill掉,系統(tǒng)不會自動重啟該服務(wù)。
(2)START_STICKY:如果Service進(jìn)程被kill掉,保留Service的狀態(tài)為開始狀態(tài),但不保留遞送的intent對象。隨后系統(tǒng)會嘗試重新創(chuàng)建Service,由于服務(wù)狀態(tài)為開始狀態(tài),所以創(chuàng)建服務(wù)后一定會調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到Service,那么參數(shù)Intent將為null。
(3)START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,系統(tǒng)會自動重啟該服務(wù),并將Intent的值傳入。
四、綁定Service
1、bindService介紹:
應(yīng)用程序組件(客戶端)通過調(diào)用bindService()方法能夠綁定服務(wù),然后Android系統(tǒng)會調(diào)用服務(wù)的onBind()回調(diào)方法,則個方法會返回一個跟服務(wù)器端交互的Binder對象。
這個綁定是異步的,bindService()方法立即返回,并且不給客戶端返回IBinder對象。要接收IBinder對象,客戶端必須創(chuàng)建一個ServiceConnection類的實例,并且把這個實例傳遞給bindService()方法。ServiceConnection對象包含了一個系統(tǒng)調(diào)用的傳遞IBinder對象的回調(diào)方法。
注意:只有Activity、Service、Content Provider能夠綁定服務(wù);BroadcastReceiver廣播接收器不能綁定服務(wù)。
2、bindService()、unbindService()
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder; //在這里返回新建的MyBinder類
}
//MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度
class MyBinder extends Binder {
public void startDownload() {
Log.d("TAG", "startDownload() executed");
// 執(zhí)行具體的下載任務(wù)
}
public int getProgress(){
Log.d("TAG", "getProgress() executed");
return 0;
}
}
}
新建一個MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度。當(dāng)然,這里只是兩個模擬方法,并沒有實現(xiàn)真正的功能,我們通過打印日志的形式來體現(xiàn)。
接著創(chuàng)建MyBinder的實例,然后在onBind()方法里返回這個實例。這樣綁定服務(wù)的組件通過獲取這個IBinder類型的實例,從而調(diào)用Service里面的方法。
private MyService.MyBinder myBinder;
//匿名內(nèi)部類:服務(wù)連接對象
private ServiceConnection connection = new ServiceConnection() {
//當(dāng)服務(wù)異常終止時會調(diào)用。注意,解除綁定服務(wù)時不會調(diào)用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//和服務(wù)綁定成功后,服務(wù)會回調(diào)該方法
@Override
public void onServiceConnected(ComponentName name, IBinder ) {
myBinder = (MyService.MyBinder) service;
//在Activity中調(diào)用Service里面的方法
myBinder.startDownload();
myBinder.getProgress();
}
};
Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button3_bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.button4_unbind_service:
unbindService(connection);
break;
default:
break;
}
}
首先創(chuàng)建了一個ServiceConnection的匿名類,在里面重寫了onServiceConnected()方法和onServiceDisconnected()方法,如果當(dāng)前Activity與服務(wù)連接成功后,服務(wù)會回調(diào)onServiceConnected()方法。
在onServiceConnected()方法中,我們又通過向下轉(zhuǎn)型得到了MyBinder的實例,有了這個實例,Activity和Service之間的關(guān)系就變得非常緊密了。現(xiàn)在我們可以在Activity中根據(jù)具體的場景來調(diào)用MyBinder中的任何public方法,即實現(xiàn)了Activity調(diào)用Service中的功能。
Activity和Service真正進(jìn)行關(guān)聯(lián),是在Bind Service按鈕的點擊事件里完成的。這里需要構(gòu)建一個Intent對象,然后調(diào)用bindService()方法將Activity和Service進(jìn)行綁定。bindService()方法接收三個參數(shù),第一個參數(shù)就是剛剛構(gòu)建出的Intent對象,第二個參數(shù)是前面創(chuàng)建出的ServiceConnection的實例,第三個參數(shù)是一個標(biāo)志位,這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后會自動創(chuàng)建Service(即使之前沒有創(chuàng)建Service也沒有關(guān)系),這會使得MyService中的onCreate()方法得到執(zhí)行,但onStartCommand()方法不會執(zhí)行。
-
如果想解除Activity和Service之間的關(guān)聯(lián),調(diào)用一下unbindService()方法就可以了,這也是Unbind Service按鈕的點擊事件里實現(xiàn)的邏輯。
image.png
運行一下程序,點擊一下Bind Service按鈕。可以看到,只點擊了Bind Service按鈕,但是oncreate()方法得到了執(zhí)行,而onStartCommand()方法不會執(zhí)行。
注意:任何一個Service在整個應(yīng)用程序范圍內(nèi)都是通用的,即MyService不僅可以和MainActivity建立關(guān)聯(lián),還可以和任何一個Activity建立關(guān)聯(lián),而且在建立關(guān)聯(lián)時它們都可以獲取到相同的MyBinder實例。
五、銷毀Service
1、關(guān)于銷毀
根據(jù)上面的知識,我們介紹了銷毀Service的第一種情況:現(xiàn)在卸載程序,重新運行程序,點擊Start Service按鈕啟動Service,再點擊Stop Service按鈕停止Service,這樣MyService就被銷毀了:

卸載程序,重新開始。那么如果我們只點擊的Bind Service按鈕呢?由于在綁定Service的時候指定的標(biāo)志位是BIND_AUTO_CREATE,說明點擊Bind Service按鈕的時候Service也會被創(chuàng)建,這時應(yīng)該怎么銷毀Service呢?其實也很簡單,點擊一下Unbind Service按鈕,將Activity和Service的關(guān)聯(lián)解除就可以了:

以上這兩種銷毀的方式都很好理解。那么如果我們既點擊了Start Service按鈕,又點擊了Bind Service按鈕會怎么樣呢?這個時候你會發(fā)現(xiàn),不管你是單獨點擊Stop Service按鈕還是Unbind Service按鈕,Service都不會被銷毀,必要將Unbind Service按鈕和Stop Service按鈕都點擊一下(沒有先后順序),Service才會被銷毀。也就是說,點擊Stop Service按鈕只會讓Service停止,點擊Unbind Service按鈕只會讓Service和Activity解除關(guān)聯(lián),一個Service必須要在既沒有和任何Activity關(guān)聯(lián)又處理停止?fàn)顟B(tài)的時候才會被銷毀。
2、Unbind銷毀的問題
點擊Unbind Service按鈕后,再次點擊Unbind Service按鈕按鈕引發(fā)的問題:
假設(shè)現(xiàn)在Service和Activity已經(jīng)相關(guān)聯(lián)了,點擊Unbind Service按鈕能夠解除綁定,如果繼續(xù)點擊Unbind Service按鈕,程序會異常退出,這說明代碼不夠完善,我們需要在代碼中加一個判斷是否綁定的標(biāo)記mBound。優(yōu)化后的代碼如下:
boolean mBound = false; //一開始,并沒有和Service綁定.這個參數(shù)是用來顯示綁定狀態(tài)
//匿名內(nèi)部類:服務(wù)連接對象
private ServiceConnection connection = new ServiceConnection() {
//當(dāng)服務(wù)異常終止時會調(diào)用。注意,解除綁定服務(wù)時不會調(diào)用
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false; //服務(wù)異常終止時,狀態(tài)為未綁定
}
//和服務(wù)綁定成功后,服務(wù)會回調(diào)該方法
@Override
public void onServiceConnected(ComponentName name, IBinder ) {
myBinder = (MyService.MyBinder) service;
//在Activity中調(diào)用Service里面的方法
myBinder.startDownload();
myBinder.getProgress();
mBound = true; //true說明是綁定狀態(tài)
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button3_bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.button4_unbind_service:
//如果和Service是綁定的狀態(tài),就解除綁定。
if(mBound){
unbindService(connection);
mBound=false;
}
break;
default:
break;
}
}
六、started服務(wù)與bind服務(wù)總結(jié)
區(qū)別一:生命周期
- 通過started方式的服務(wù)會一直運行在后臺,需要由組件本身或外部組件來停止服務(wù)才會以結(jié)束運行。
- bind方式的服務(wù),生命周期就要依賴綁定的組件。
區(qū)別二:參數(shù)傳遞
- started服務(wù)可以給啟動的服務(wù)對象傳遞參數(shù),但無法獲取服務(wù)中方法的返回值。
- bind服務(wù)可以給啟動的服務(wù)對象傳遞參數(shù),也可以通過綁定的業(yè)務(wù)對象獲取返回結(jié)果。
實際開發(fā)中的技巧;
- 第一次先使用started方式來啟動一個服務(wù)。
- 之后可以使用bind的方式綁定服務(wù),從而可以直接調(diào)用業(yè)務(wù)方法獲取返回值。
生命周期

一旦在項目的任何位置調(diào)用了Context的startService()方法,相應(yīng)的服務(wù)就會啟動起來,并回調(diào)onstartCommand()方法。如果這個服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會先于onstartCommand()方法執(zhí)行。服務(wù)啟動過后,會一直保持運行狀態(tài),直到stopService()或stopself()方法被調(diào)用。注意雖然每次調(diào)用一次startService()方法,onstartCommand()方法就會以執(zhí)行一次,但實際上每個服務(wù)都只會存在一個實例。所以不管你調(diào)用了多少次startService()方法,只需調(diào)用一次stopService()或stopself()方法,服務(wù)就會停止。
另外,還可以調(diào)用Context的bindService()來獲取一個服務(wù)的持久連接,這時就會回調(diào)服務(wù)中的onBind()方法。類似地,如果這個服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會先于onBind()方法執(zhí)行。之后調(diào)用方可以獲取到onBind()方法里返回的IBinder對象的實例,這樣,就能自由地和服務(wù)進(jìn)行通信了。只要調(diào)用方和服務(wù)之間的連接沒有斷開,服務(wù)就會一直保持運行狀態(tài)。
android 5.0 之后隱式啟動service
