Service在Android開發的過程中,相信小夥伴們都沒有少與它互動,如此重要的組件,現在來一起在看看它吧
一、什麼是Service?
Service是Android的四大組件之一,屬於計算型的組件,它提供在後臺需要上時間運行的服務,例如音樂播放、文件下載等,有着無用戶界面、後臺運行、生命週期長等特點。
二、Service的生命週期是什麼樣子的?
Service的生命週期與Activity生命週期類似,需要關注兩組方法
一組是手動調用的方法
startService() 啓動服務,stopService() 關閉服務, bindService() 綁定服務, unbindService() 解綁服務
一組是自動調用的方法
onCreate() 創建服務, onStartCommand() 開始服務, onDestroy() 銷燬服務, onBind() 綁定服務, onUnbind() 解綁服務
常見的應用場景
1.啓動服務(只使用startService):手動調用startService --> oncreate --> onStartCommand --> Service運行 --> 手動調用stopService --> onDestory 需要注意的是:如果一個Service被startService多次啓動,那麼onCreate也只會調用一次,即Service實例只有1個;整個生命週期裏,只有onStartCommand可多次調用,其他只能調用1次;onStartCommand調用的次數等於startService調用次數。
2.綁定服務(只使用bindService):手動調用bindService --> onCreate --> onBind --> Service綁定後運行 --> 手動調用 unBIndService --> onUnbind --> onDestory 需要注意的是:客戶端通過一個IBinder接口與服務進行通信;若一個Service被BIndService多次啓動,那麼onCreate也只會調用1次,即Service的實例只有一個;多個客戶端可以綁定在一個服務上,當所有的客戶端都解綁後,系統會銷燬服務,服務不需要手動終止。
特別注意:
關於操作Service
startService stopService 只能開啓&關閉Service,但無法操作Service;
bindService unBindService 除了綁定Service 還能操作Service;
關於Service何時銷燬
startService開啓的Service,調用者退出後,Service仍然存在
bindService開啓的Service,調用者退出後,Service隨着調用者的退出銷燬
若在無解綁的前提下調用stopService是無法停止服務的
關於onStartCommand返回值
onStartCommand必須返回一個整數,用來描述系統在殺死服務後應該如何運行
1)START_NOT_STICKY:不會重建服務,除非還存在未發送的Intent。當服務不再是必須的,並且應用程序能夠簡單的重啓那些未完成的工作時,這是避免服務運行的最安全的選項
2)START_STICKY:重建服務並且調用onStartCommond,但不會再次送入上一個intent,而是用nullintent來調用onStartCommand,除非還有啓動服務的intent未發送完,那麼這些剩下的intent會繼續發送(適用於媒體播放器類似的服務,它們不執行命令,但需要一直運行並隨時待命)
3)START_REDELIVER_INTENT:重建服務並且用上一個已經發送過的Intent調用onStartCommand,任何未發送完的intent也都會依次送入(適用於那些需要立即恢復工作的活躍服務,比如文件下載)
三、Service的分類
1.按運行地點分類可以分爲本地服務和遠程服務
本地服務(Local Service)
特點:運行在主線程,主線程被終止後,服務也會終止
優點:節約資源,通信方便,因爲在同1個進程不要IPC和AIDL
缺點:限制性大,主進程被終止後,服務也會終止
應用場景:需依附於某個進程的服務,也是最常用的服務類型,如音樂播放
遠程服務(Remote Service)
特點:運行在獨立的線程,服務常駐在後臺,不受其他Activity的影響
優點:靈活,服務常駐在後臺,不受其他Activity影響
缺點:資源消耗,單獨進程,使用IPC和AIDL編寫複雜
應用場景:系統級別的服務
按運行類型分類可以分爲前臺服務和後臺服務
前臺服務
特點:在通知欄顯示通知,用戶可見
應用場景:服務使用時,需讓用戶知道操作的邏輯,如音樂播放服務,服務被終止時,通知欄的服務也會消失
後臺服務
特點:處於後臺的服務,用戶不可見
應用場景:服務使用時不需要讓用戶知道操作的邏輯,如天氣更新,日期同步等,服務被終止時,用戶沒有感知
按功能分類可以分爲可通信服務和不可通信服務
不可通信的後臺服務,調用者退出後Service仍然存在
特點:用startService
應用場景:服務不需要於Activity和Service通信
可通信的後臺服務
1)用bindService啓動
特點:調用者退出後,隨着調用者銷燬
應用場景:服務需要與Activity Service通信,需控制服務開始的時刻
用bindService啓動服務節約系統資源,第一次bindService時纔會創建服務的實例並且運行,特別當服務是遠程服務時,該效果更加明顯
服務只是一個遠程接口,供客戶端遠程調用執行方法
BoradcastReciever也可以完成這些需求,但是有以下缺點:若交互頻繁,容易造成性能問題,且BoradcastReciever本身執行時間非常短,很可能執行到一半,後面的代碼便不會執行了
2)使用startService,bindService啓動
特點:調用者退出後,隨着調用者的銷燬
應用場景:需與Activity和Service通信,不需要控制服務的開始時刻(服務一開始便運行)
四、具體使用
1.本地Service
-->新建子類繼承Service類(需重寫父類的onCreate onStartCommand onDestory和onBind方法)
-->構建用於Service的Intent對象
-->調用stratService啓動Service,調用stopService停止服務
-->在清單文件裏註冊Service
創建服務
public class MyService extends Service {
//啓動Service之後,就可以在onCreate()或onStartCommand()方法裏去執行一些具體的邏輯
//由於這裏作Demo用,所以只打印一些語句
@Override
public void onCreate() {
super.onCreate();
System.out.println("執行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("執行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("執行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
啓停服務
//構建啓動服務的Intent對象
Intent startIntent = new Intent(this, MyService.class);
//調用startService()方法-傳入Intent對象,以此啓動服務
startService(startIntent);
//構建停止服務的Intent對象
Intent stopIntent = new Intent(this, MyService.class);
//調用stopService()方法-傳入Intent對象,以此停止服務
stopService(stopIntent);
註冊服務
//註冊Service服務
<service android:name=".MyService">
</service>
2.可通信的服務Service
上面介紹的代碼,只能用作單機使用,無法與Activity進行通信,接下來將增加與Activity通信的功能,即使用綁定Service服務(Binder類、bindService()、onBind()、unbindService()、onUnbind())
-->新建一個子類繼承Binder類,寫入與Activity關聯需要的方法,創建實例
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
System.out.println("執行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("執行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("執行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("執行了onBind()");
//返回實例
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("執行了onUnbind()");
return super.onUnbind(intent);
}
//新建一個子類繼承自Binder類
class MyBinder extends Binder {
public void service_connect_Activity() {
System.out.println("Service關聯了Activity,並在Activity執行了Service的方法");
}
}
}
-->綁定與解綁的方法
//構建綁定服務的Intent對象
Intent bindIntent = new Intent(this, MyService.class);
//調用bindService()方法,以此停止服務
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//參數說明
//第一個參數:Intent對象
//第二個參數:Serviceconnection實例
//第三個參數:標誌位
//這裏傳入BIND_AUTO_CREATE表示在Activity和Service建立關聯後自動創建Service
//這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行
//調用unbindService()解綁服務
//參數是Serviceconnection實例
unbindService(connection);
-->ServiceConnection匿名類
//創建ServiceConnection的匿名類
private ServiceConnection connection = new ServiceConnection() {
//重寫onServiceConnected()方法和onServiceDisconnected()方法
//在Activity與Service建立關聯和解除關聯的時候調用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity與Service解除關聯的時候調用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//實例化Service的內部類myBinder
//通過向下轉型得到了MyBinder的實例
myBinder = (MyService.MyBinder) service;
//在Activity調用Service類的方法
myBinder.service_connect_Activity();
}
};
3.前臺Service
在狀態欄可見,擁有較高的優先級
對上面的代碼稍加修改
//添加下列代碼將後臺Service變成前臺Service
//構建"點擊通知後打開MainActivity"的Intent對象
Intent notificationIntent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);
//新建Builer對象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前臺服務通知的標題");//設置通知的標題
builer.setContentText("前臺服務通知的內容");//設置通知的內容
builer.setSmallIcon(R.mipmap.ic_launcher);//設置通知的圖標
builer.setContentIntent(pendingIntent);//設置點擊通知後的操作
Notification notification = builer.getNotification();//將Builder對象轉變成普通的notification
startForeground(1, notification);//讓Service變成前臺Service,並在系統的狀態欄顯示出來
4.遠程Service
多個應用程序共享一個後臺服務(遠程服務),即一個遠程Service與多個應用程序組件(四大組件)進行跨進程通信
爲了讓Service與多個應用程序的組件進行跨進程通信(IPC),需要使用AIDL(Android Interface Definition Language,即Android接口定義語言;用於讓某個Service與多個應用程序組件之間進行跨進程通信,從而可以實現多個應用程序共享同一個Service的功能。)
在多進程通信中,最基礎的模型存在兩個進程角色:服務端和客戶端
服務端(Service)
-->新建AIDL文件,並聲明該服務需要向客戶端提供的接口
-->在Service子類中實現AIDL中定義的接口方法,並定義相關的生命週期方法
-->在清單文件中註冊服務,並且聲明服務爲遠程服務
客戶端(Client)
-->拷貝服務端的AIDL文件到目錄下
-->使用Stub.asInterface接口獲取服務端的Binder,根據需要調用服務端提供的接口方法
-->通過Intent指定服務端的服務名稱和所在包,綁定遠程Service
服務端代碼
注意這裏是新建AIDL文件而不是Java類,完成之後make project
// 在新建的AIDL_Service1.aidl裏聲明需要與Activity進行通信的方法
interface AIDL_Service1 {
void AIDL_Service();
}
//AIDL中支持以下的數據類型
//1. 基本數據類型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 對象的元素必須是AIDL支持的數據類型;
//4. AIDL自動生成的接口(需要導入-import)
//5. 實現android.os.Parcelable 接口的類(需要導入-import)
在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreate、onBind等)
public class MyService extends Service {
// 實例化AIDL的Stub類(Binder的子類)
AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {
//重寫接口裏定義的方法
@Override
public void AIDL_Service() throws RemoteException {
System.out.println("客戶端通過AIDL與遠程後臺成功通信");
}
};
//重寫與Service生命週期的相關方法
@Override
public void onCreate() {
super.onCreate();e
System.out.println("執行了onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("執行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("執行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("執行了onBind()");
//在onBind()返回繼承自Binder的Stub類型的Binder,非常重要
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("執行了onUnbind()");
return super.onUnbind(intent);
}
}
在清單文件裏註冊
<service
android:name=".MyService"
android:process=":remote" //將本地服務設置成遠程服務
android:exported="true" //設置可被其他進程調用
>
//此處Intent的action必須寫成“服務器端包名.aidl文件名”
<intent-filter>
<action android:name="你的包名.service_server.AIDL_Service1"/>
</intent-filter>
</service>
客戶端
--> 將服務端的AIDL文件所在的包複製到客戶端目錄下(Project/app/src/main),並進行編譯
最好是複製,什麼都不要改,否則容易出問題
-->使用Stub.asInterface接口獲取服務器的Binder
-->通過Intent指定服務端的服務名稱和所在包,進行Service綁定;
-->根據需要調用服務提供的接口方法。
public class MainActivity extends AppCompatActivity {
private Button bindService;
//定義aidl接口變量
private AIDL_Service1 mAIDL_Service;
//創建ServiceConnection的匿名類
private ServiceConnection connection = new ServiceConnection() {
//重寫onServiceConnected()方法和onServiceDisconnected()方法
//在Activity與Service建立關聯和解除關聯的時候調用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity與Service建立關聯時調用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//使用AIDLService1.Stub.asInterface()方法獲取服務器端返回的IBinder對象
//將IBinder對象傳換成了mAIDL_Service接口對象
mAIDL_Service = AIDL_Service1.Stub.asInterface(service);
try {
//通過該對象調用在MyAIDLService.aidl文件中定義的接口方法,從而實現跨進程通信
mAIDL_Service.AIDL_Service();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
//設置綁定服務的按鈕
bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定
//參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"
Intent intent = new Intent("你的包名.service_server.AIDL_Service1");
//Android5.0後無法只通過隱式Intent綁定遠程Service
//需要通過setPackage()方法指定包名
intent.setPackage("你的包名.service_server");
//綁定服務,傳入intent和ServiceConnection對象
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
}
這樣便完成了簡單的客戶端和服務端的通信
通過這邊博客對Android Service的知識點進行了一個較爲全面的梳理,希望對小夥伴們有所幫助~