參考:
Android Service完全解析,關(guān)于服務(wù)你所需知道的一切(上)
Android Service完全解析,關(guān)于服務(wù)你所需知道的一切(下)
關(guān)于Android Service真正的完全詳解,你需要知道的一切
閱讀了這兩(三)篇解析之后,將其中涉及到的東西以自己能理解的方式記錄下來(lái)。
介紹
Service主要用于在后臺(tái)處理一些耗時(shí)的邏輯,或者去執(zhí)行某些需要長(zhǎng)期運(yùn)行的任務(wù)。必要的時(shí)候我們甚至可以在程序退出的情況下,讓Service在后臺(tái)繼續(xù)保持運(yùn)行狀態(tài),如音樂(lè)播放器等。
根據(jù)服務(wù)在主線程或其他線程運(yùn)行,可以分為本地服務(wù)和遠(yuǎn)程服務(wù);
啟動(dòng)與綁定
根據(jù)服務(wù)運(yùn)行的兩種形式,可以分為啟動(dòng)方式和綁定方式:
- 啟動(dòng)
當(dāng)應(yīng)用組件(如 Activity)通過(guò)調(diào)用startService()
啟動(dòng)服務(wù)時(shí),服務(wù)即處于“啟動(dòng)”狀態(tài)。一旦啟動(dòng),服務(wù)即可在后臺(tái)無(wú)限期運(yùn)行,即使啟動(dòng)服務(wù)的組件已被銷毀也不受影響,除非手動(dòng)調(diào)用才能停止服務(wù), 已啟動(dòng)的服務(wù)通常是執(zhí)行單一操作,而且不會(huì)將結(jié)果返回給調(diào)用方,和啟動(dòng)源沒(méi)什么聯(lián)系。 - 綁定
當(dāng)應(yīng)用組件通過(guò)調(diào)用bindService()
綁定到服務(wù)時(shí),服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個(gè)客戶端-服務(wù)器接口(ServiceConnection),通過(guò)這個(gè)借口中的相關(guān)方法可以獲取到服務(wù)的信息,執(zhí)行相關(guān)方法等,允許組件與服務(wù)進(jìn)行交互、發(fā)送請(qǐng)求、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作。 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí),綁定服務(wù)才會(huì)運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù),但全部取消綁定后,該服務(wù)即會(huì)被銷毀。
雖然服務(wù)的狀態(tài)有啟動(dòng)和綁定兩種,但實(shí)際上一個(gè)服務(wù)可以同時(shí)是這兩種狀態(tài),也就是說(shuō),它既可以是啟動(dòng)服務(wù)(以無(wú)限期運(yùn)行),也可以是綁定服務(wù)服務(wù),問(wèn)題只是在于是否實(shí)現(xiàn)了相應(yīng)的回調(diào)方法:onStartCommand()(允許組件啟動(dòng)服務(wù))和 onBind()(允許綁定服務(wù))。有點(diǎn)需要注意的是Android系統(tǒng)僅會(huì)為一個(gè)Service創(chuàng)建一個(gè)實(shí)例對(duì)象,所以不管是啟動(dòng)服務(wù)還是綁定服務(wù),操作的是同一個(gè)Service實(shí)例,而且由于綁定服務(wù)或者啟動(dòng)服務(wù)執(zhí)行順序問(wèn)題將會(huì)出現(xiàn)以下兩種情況:
- 先綁定服務(wù)后啟動(dòng)服務(wù)
如果當(dāng)前Service實(shí)例先以綁定狀態(tài)運(yùn)行,然后再以啟動(dòng)狀態(tài)運(yùn)行,那么綁定服務(wù)將會(huì)轉(zhuǎn)為啟動(dòng)服務(wù)運(yùn)行,這時(shí)如果之前綁定的宿主(Activity)被銷毀了,也不會(huì)影響服務(wù)的運(yùn)行,服務(wù)還是會(huì)一直運(yùn)行下去,指定收到調(diào)用停止服務(wù)或者內(nèi)存不足時(shí)才會(huì)銷毀該服務(wù)。 - 先啟動(dòng)服務(wù)后綁定服務(wù)
如果當(dāng)前Service實(shí)例先以啟動(dòng)狀態(tài)運(yùn)行,然后再以綁定狀態(tài)運(yùn)行,當(dāng)前啟動(dòng)服務(wù)并不會(huì)轉(zhuǎn)為綁定服務(wù),但是還是會(huì)與宿主綁定,只是即使宿主解除綁定后,服務(wù)依然按啟動(dòng)服務(wù)的生命周期在后臺(tái)運(yùn)行,直到有Context調(diào)用了stopService()或是服務(wù)本身調(diào)用了stopSelf()方法抑或內(nèi)存不足時(shí)才會(huì)銷毀服務(wù)。
生命周期
使用方法
Service有幾種不同的運(yùn)行方式,可以在主線程或其他線程中運(yùn)行,可以跨進(jìn)程交互,但整體來(lái)說(shuō)不外乎在Service中編寫相對(duì)應(yīng)的回調(diào)方法,然后在Activity中啟動(dòng)或綁定,如果只是啟動(dòng),則之后Activity只能停止,而不能做出交互,只有綁定方式,才可以在ServiceConnection中獲取到Service定義的Binder對(duì)象(或者Service自己也是可以的)進(jìn)行交互。
繼承Service
編寫自己的自定義Service
public class MyService extends Service {
public static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 只在啟動(dòng)模式下執(zhí)行
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
}
@Override
public IBinder onBind(Intent intent) {
// 只在綁定時(shí)執(zhí)行,需要編寫B(tài)inder對(duì)象在這里返回
return null;
}
}
啟動(dòng)模式
- 在
AndroidManifest.xml
中注冊(cè)(application
標(biāo)簽之內(nèi))
<service android:name="com.example.servicetest.MyService" >
- 在需要的時(shí)機(jī)使用Intent啟動(dòng)或停止Service
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
因?yàn)榫帉懙腟ervice沒(méi)有編寫onBind()
方法,也沒(méi)有綁定任何東西,啟動(dòng)之后就和啟動(dòng)它的Activity沒(méi)有什么關(guān)系了;
啟動(dòng)過(guò)程:onCreate()->onStartCommand()
,在啟動(dòng)之后再startService,就只有onStartCommand()
執(zhí)行了。
在服務(wù)的內(nèi)部可以調(diào)用stopSelf()
方法停止當(dāng)前服務(wù)。
綁定模式
要和Activity關(guān)聯(lián)起來(lái)就需要實(shí)現(xiàn)onBind()
方法,首先需要編寫一個(gè)自定義的Binder繼承自Binder基類:
class MyBinder extends Binder {
public void startDoSth() {
Log.d("TAG", "do sth");
// do sth
}
}
這個(gè)類將在Service對(duì)象創(chuàng)建時(shí)實(shí)例化并用于和Service綁定,修改之前的Service代碼:
private MyBinder mBinder = new MyBinder();
...
...
@Override
public IBinder onBind(Intent intent) {
// 此處把mBinder作為一個(gè)IBinder返回,在后面被connection獲取
return mBinder;
}
同時(shí)在Activity中還需要實(shí)現(xiàn)一個(gè)ServiceConnection
來(lái)獲取Binder
并執(zhí)行里面的方法完成這一過(guò)程:
private MyService.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
//Android 系統(tǒng)會(huì)在與服務(wù)的連接意外中斷時(shí)(例如當(dāng)服務(wù)崩潰或被終止時(shí))調(diào)用該方法。注意:當(dāng)客戶端取消綁定時(shí),系統(tǒng)“絕對(duì)不會(huì)”調(diào)用該方法。
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 綁定之后進(jìn)行的操作,應(yīng)該是自動(dòng)獲取到IBinder類型的service,實(shí)際上就是MyBinder,所以直接強(qiáng)制類型轉(zhuǎn)換
myBinder = (MyService.MyBinder) service;
myBinder.startDoSth();
}
};
到這里只是寫好了用于綁定的準(zhǔn)備工作和綁定完成之后的操作,而真正的綁定過(guò)程如下:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
bindService()方法接收三個(gè)參數(shù),第一個(gè)參數(shù)就是剛剛構(gòu)建出的Intent對(duì)象,第二個(gè)參數(shù)是前面創(chuàng)建出的ServiceConnection
的實(shí)例,第三個(gè)參數(shù)是一個(gè)標(biāo)志位,這里傳入BIND_AUTO_CREATE
表示在Activity和Service建立關(guān)聯(lián)后自動(dòng)創(chuàng)建Service,這會(huì)使得MyService中的onCreate()
方法得到執(zhí)行,但onStartCommand()
方法不會(huì)執(zhí)行。所以綁定之后的流程是onCreate()->startDoSth()
。
注意到這里并沒(méi)有onStartCommand()
,而是執(zhí)行onServiceConnected()
中的代碼。
停止Service:
unbindService(connection);
// 這里的service只創(chuàng)建了,并沒(méi)有start,所以直接解綁就可以
注意:
Stop Service只會(huì)讓Service停止,Unbind Service按鈕只會(huì)讓Service和Activity解除關(guān)聯(lián),一個(gè)Service必須要在既沒(méi)有和任何Activity關(guān)聯(lián)又處理停止?fàn)顟B(tài)的時(shí)候才會(huì)被銷毀。
宿主(Activity)解除綁定后,綁定服務(wù)就會(huì)被銷毀
遠(yuǎn)程Service(無(wú)法綁定)
將一個(gè)普通的Service轉(zhuǎn)換成遠(yuǎn)程Service其實(shí)非常簡(jiǎn)單,只需要在注冊(cè)Service的時(shí)候?qū)⑺腶ndroid:process屬性指定成:remote
就可以了
<service
android:name="com.example.servicetest.MyService"
android:process=":remote" >
</service>
遠(yuǎn)程Service不在主線程中運(yùn)行,耗時(shí)操作不會(huì)阻塞進(jìn)程,但是Activity和Service運(yùn)行在兩個(gè)不同的進(jìn)程當(dāng)中,也就不能再使用傳統(tǒng)的建立關(guān)聯(lián)的方式,根本無(wú)法綁定了。所以這時(shí)只能start而不能使用bind方法來(lái)使用Service。
綁定遠(yuǎn)程Service(改進(jìn)版/跨進(jìn)程通信)
使用AIDL
此處使用了AIDL(Android Interface Definition Language),是Android接口定義語(yǔ)言的意思,它可以用于讓某個(gè)Service與多個(gè)應(yīng)用程序組件之間進(jìn)行跨進(jìn)程通信,從而可以實(shí)現(xiàn)多個(gè)應(yīng)用程序共享同一個(gè)Service的功能。
新建aidl,之后會(huì)自動(dòng)生成這樣一個(gè)java文件:
package com.example.servicetest;
interface MyAIDLService {
int plus(int a, int b);
String toUpperCase(String str);
}
然后修改MyService中的代碼,在里面實(shí)現(xiàn)定義好的MyAIDLService接口(沒(méi)弄明白Stub哪來(lái)的,此處理解為一個(gè)存根),此處把實(shí)例化Binder改成了實(shí)例化一個(gè)Stub:
MyAIDLService.Stub mBinder = new Stub() {
@Override
public String toUpperCase(String str) throws RemoteException {
if (str != null) {
return str.toUpperCase();
}
return null;
}
@Override
public int plus(int a, int b) throws RemoteException {
return a + b;
}
};
然后在onBind()
方法中將MyAIDLService.Stub的實(shí)現(xiàn)返回。因?yàn)?strong>Stub其實(shí)就是Binder的子類,所以在onBind()
方法中可以直接返回Stub的實(shí)現(xiàn)。
修改connection:
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myAIDLService = MyAIDLService.Stub.asInterface(service);
// 調(diào)用Service中的方法
try {
int result = myAIDLService.plus(3, 5);
String upperStr = myAIDLService.toUpperCase("hello world");
Log.d("TAG", "result is " + result);
Log.d("TAG", "upperStr is " + upperStr);
} catch (RemoteException e) {
e.printStackTrace();
}
}
關(guān)于Stub:
j2ee里面的stub是這樣說(shuō)的..為屏蔽客戶調(diào)用遠(yuǎn)程主機(jī)上的對(duì)象,必須提供某種方式來(lái)模擬本地對(duì)象,這種本地對(duì)象稱為存根(stub),存根負(fù)責(zé)接收本地方法調(diào)用,并將它們委派給各自的具體實(shí)現(xiàn)對(duì)象
此時(shí)依然是調(diào)用bindService()
來(lái)綁定服務(wù),在一個(gè)Activity里調(diào)用了同一個(gè)應(yīng)用程序的Service里的方法。
跨進(jìn)程
在另一個(gè)應(yīng)用程序中去綁定Service的時(shí)候并沒(méi)有MyService這個(gè)類,這時(shí)就必須使用到隱式Intent了。現(xiàn)在修改AndroidManifest.xml中的代碼,給MyService加上一個(gè)action
<service
android:name="com.example.servicetest.MyService"
android:process=":remote" >
<intent-filter>
<action android:name="com.example.servicetest.MyAIDLService"/>
</intent-filter>
</service>
在另一個(gè)程序中,需要復(fù)制一份MyAIDLService.aidl
文件,注意要將原有的包路徑一起拷貝過(guò)來(lái),綁定的代碼修改如下:
Intent intent = new Intent("com.example.servicetest.MyAIDLService").setPakage("com.example.servicetest");;
bindService(intent, connection, BIND_AUTO_CREATE);
將Intent的action指定成了com.example.servicetest.MyAIDLService。
使用Messenger
- 服務(wù)實(shí)現(xiàn)一個(gè) Handler,由其接收來(lái)自客戶端的每個(gè)調(diào)用的回調(diào)
/**
* 用于接收從客戶端傳遞過(guò)來(lái)的數(shù)據(jù)
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Log.i(TAG, "thanks,Service had receiver message from client!");
break;
default:
super.handleMessage(msg);
}
}
}
- Handler 用于創(chuàng)建 Messenger 對(duì)象(對(duì) Handler 的引用)
/**
* 創(chuàng)建Messenger并傳入Handler實(shí)例對(duì)象
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
- Messenger 創(chuàng)建一個(gè) IBinder,服務(wù)通過(guò) onBind() 使其返回客戶端
/**
* 當(dāng)綁定Service時(shí),該方法被調(diào)用,將通過(guò)mMessenger返回一個(gè)實(shí)現(xiàn)
* IBinder接口的實(shí)例對(duì)象
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service is invoke onBind");
return mMessenger.getBinder();
}
- 客戶端使用 IBinder 將 Messenger(引用服務(wù)的 Handler)實(shí)例化,然后使用Messenger將 Message 對(duì)象發(fā)送給服務(wù)
public void onServiceConnected(ComponentName className, IBinder service) {
/**
* 通過(guò)服務(wù)端傳遞的IBinder對(duì)象,創(chuàng)建相應(yīng)的Messenger
* 通過(guò)該Messenger對(duì)象與服務(wù)端進(jìn)行交互
*/
mService = new Messenger(service);
mBound = true;
}
- 服務(wù)在其 Handler 中(在 handleMessage() 方法中)接收每個(gè) Message
前臺(tái)服務(wù)
前臺(tái)服務(wù)被認(rèn)為是用戶主動(dòng)意識(shí)到的一種服務(wù),因此在內(nèi)存不足時(shí),系統(tǒng)也不會(huì)考慮將其終止。 前臺(tái)服務(wù)必須為狀態(tài)欄提供通知,狀態(tài)欄位于“正在進(jìn)行”標(biāo)題下方,這意味著除非服務(wù)停止或從前臺(tái)刪除,否則不能清除通知。
Android官方給我們提供了兩個(gè)方法,分別是startForeground()和stopForeground(),這兩個(gè)方式解析如下:
-
startForeground(int id, Notification notification)
該方法的作用是把當(dāng)前服務(wù)設(shè)置為前臺(tái)服務(wù),其中id參數(shù)代表唯一標(biāo)識(shí)通知的整型數(shù),需要注意的是提供給 startForeground() 的整型 ID 不得為 0,而notification是一個(gè)狀態(tài)欄的通知。 -
stopForeground(boolean removeNotification)
該方法是用來(lái)從前臺(tái)刪除服務(wù),此方法傳入一個(gè)布爾值,指示是否也刪除狀態(tài)欄通知,true為刪除。 注意該方法并不會(huì)停止服務(wù)。 但是,如果在服務(wù)正在前臺(tái)運(yùn)行時(shí)將其停止,則通知也會(huì)被刪除。
Myservice.java
/**
* Notification
*/
public void createNotification(){
//使用兼容版本
NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
//設(shè)置狀態(tài)欄的通知圖標(biāo)
builder.setSmallIcon(R.mipmap.ic_launcher);
//設(shè)置通知欄橫條的圖標(biāo)
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.screenflash_logo));
//禁止用戶點(diǎn)擊刪除按鈕刪除
builder.setAutoCancel(false);
//禁止滑動(dòng)刪除
builder.setOngoing(true);
//右上角的時(shí)間顯示
builder.setShowWhen(true);
//設(shè)置通知欄的標(biāo)題內(nèi)容
builder.setContentTitle("I am Foreground Service!!!");
//創(chuàng)建通知
Notification notification = builder.build();
//設(shè)置為前臺(tái)服務(wù)
startForeground(NOTIFICATION_DOWNLOAD_PROGRESS_ID,notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int i=intent.getExtras().getInt("cmd");
if(i==0){
if(!isRemove) {
createNotification();
}
isRemove=true;
}else {
//移除前臺(tái)服務(wù)
if (isRemove) {
stopForeground(true);
}
isRemove=false;
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
//移除前臺(tái)服務(wù)
if (isRemove) {
stopForeground(true);
}
isRemove=false;
super.onDestroy();
}
onStartCommand詳解
onStartCommand(tent intent, int flags, int startId)
參數(shù)
- intent :?jiǎn)?dòng)時(shí),啟動(dòng)組件傳遞過(guò)來(lái)的Intent,如Activity可利用Intent封裝所需要的參數(shù)并傳遞給Service
- flags:表示啟動(dòng)請(qǐng)求時(shí)是否有額外數(shù)據(jù),可選值有 0,
START_FLAG_REDELIVERY
,START_FLAG_RETRY
,0代表沒(méi)有,它們具體含義如下: -
START_FLAG_REDELIVERY
這個(gè)值代表了onStartCommand
方法的返回值為
START_REDELIVER_INTENT
,而且在上一次服務(wù)被殺死前會(huì)去調(diào)用stopSelf方法停止服務(wù)。其中START_REDELIVER_INTENT
意味著當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,則會(huì)重建服務(wù),并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用onStartCommand()
,此時(shí)Intent時(shí)有值的。 -
START_FLAG_RETRY
該flag代表當(dāng)onStartCommand
調(diào)用后一直沒(méi)有返回值時(shí),會(huì)嘗試重新去調(diào)用onStartCommand()
。 -
startId
: 指明當(dāng)前服務(wù)的唯一ID,與stopSelfResult(int startId)
配合使用,stopSelfResult
可以更安全地根據(jù)ID停止服務(wù)。
返回值
實(shí)際上onStartCommand
的返回值int類型才是最最值得注意的,它有三種可選值, START_STICKY
,START_NOT_STICKY
,START_REDELIVER_INTENT
,它們具體含義如下:
-
START_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,一段時(shí)間后內(nèi)存再次空閑時(shí),系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service,一旦創(chuàng)建成功后將回調(diào)onStartCommand
方法,但其中的Intent將是null,除非有掛起的Intent,如pendingintent,這個(gè)狀態(tài)下比較適用于不執(zhí)行命令、但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器或類似服務(wù)。 -
START_NOT_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,即使系統(tǒng)內(nèi)存再次空閑時(shí),系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service。除非程序中再次調(diào)用startService啟動(dòng)此Service,這是最安全的選項(xiàng),可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)。 -
START_REDELIVER_INTENT
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,則會(huì)重建服務(wù),并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用onStartCommand()
,任何掛起 Intent均依次傳遞。與START_STICKY
不同的是,其中的傳遞的Intent將是非空,是最后一次調(diào)用startService中的intent。這個(gè)值適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)。