1.Service簡介
服務是一個應用程序組件,可以在后臺執行長時間運行的操作,不提供用戶界面。一個應用程序組件可以啟動一個服務,它將繼續在后臺運行,即使用戶切換到另一個應用程序。此外,一個組件可以綁定到一個服務與它交互,甚至執行進程間通信(IPC)。例如,一個服務可能處理網絡通信、播放音樂、計時操作或與一個內容提供者交互,都在后臺執行。
2.Service的種類
- 按運行地點分類:
類別 | 區別 | 優點 | 缺點 | 應用 |
---|---|---|---|---|
本地服務(Local Service) | 該服務依附在主進程上 | 服務依附在主進程上而不是獨立的進程,這樣在一定程度上節約了資源,另外Local服務因為是在同一進程因此不需要IPC,也不需要AIDL。相應bindService會 方便很多。 |
主進程被Kill后,服務便會終止。 | 如:音樂播放器播放等不需要常駐的服務。 |
遠程服務(Remote Service) | 該服務是獨立的進程 | 服務為獨立的進程,對應進程名格式為所在包名加上你指定的android:process 字符串。由于是獨立的進程,因此在Activity所在進程被Kill的時候,該服務依然在運行,不受其他進程影響,有利于為多個進程提供服務具有較高的靈活性。 |
該服務是獨立的進程,會占用一定資源,并且使用AIDL進行IPC稍微麻煩一點。 | 一些提供系統服務的Service,這種Service是常駐的。 |
- 按運行類型分類:
類別 | 區別 | 應用 |
---|---|---|
前臺服務 | 會在通知欄顯示onGoing的 Notification | 當服務被終止的時候,通知一欄的 Notification 也會消失,這樣對于用戶有一定的通知作用。常見的如音樂播放服務。 |
后臺服務 | 默認的服務即為后臺服務,即不會在通知一欄顯示 onGoing的 Notification。 | 當服務被終止的時候,用戶是看不到效果的。某些不需要運行或終止提示的服務,如天氣更新,日期同步,郵件同步等。 |
- 按使用方式分類:
類別 | 區別 |
---|---|
startService啟動的服務 | 主要用于啟動一個服務執行后臺任務,不進行通信。停止服務使用stopService。 |
bindService啟動的服務 | 方法啟動的服務要進行通信。停止服務使用unbindService。 |
同時使用startService、bindService 啟動的服務 | 停止服務應同時使用stopService與unbindService。 |
3.Service的生命周期
通過這個圖可以看到,兩種啟動Service的方式以及他們的生命周期,bindService的不同之處在于當綁定的組件銷毀后,對應的service也就被kill了。
-
被啟動的服務的生命周期
一個Service被使用startService
方法啟動,不管是否調用了bindService
(綁定服務)或unbindService
(解除綁定服務)到該Service,該Service都會在后臺運行并不受影響。
一個Service被使用startService
方法啟動多少次,onCreate方法只會調用一次,onStartCommand方法將會被調用多次(與startService
的次數一致),且系統只會創建一個Service實例(結束該Service也只需要調用一次stopService
),該Service會一直在后臺運行,直至調用stopService
或調用自身的stopSelf
方法。
注:在系統資源不足的情況下,服務有可能被系統結束(kill);
被綁定的服務的生命周期
如果一個Service在某個Activity中被調用bindService
方法啟動,不論bindService
被調用幾次,Service的onCreate方法只會執行一次,同時onStartCommand方法始終不會調用。
當建立連接后,Service會一直運行,除非調用unbindService
來接觸綁定、斷開連接或調用該Service的Context不存在了(如Activity被Finish——即通過bindService
啟動的Service的生命周期依附于啟動它的Context),系統在這時會自動停止該Service。被啟動又被綁定的服務的生命周期
當一個Service在被啟動(startService
)的同時又被綁定(bindService
),該Service將會一直在后臺運行,并且不管調用幾次,onCreate方法始終只會調用一次,onStartCommand的調用次數與startService
調用的次數一致(使用bindService
方法不會調用onStartCommand)。同時,調用unBindService
將不會停止Service,必須調用stopService
或Service自身的stopSelf
來停止服務。
官方原文:
If you do allow your service to be started and bound,then when then service has been started,the System does not destory the service when all clients unbind.Instead,you must explicitly stop the service,by calling stopSelf()
or stopService()
.
如果你同意你的服務被開啟和綁定,然后當服務被開啟的時候,當所有的客戶端都解除對服務的綁定android操作系統也不會銷毀這個服務,相反的你必須顯示的調用stopSelf()或者stopService()方法來停止服務。
什么情況下使用:
如果你想要與正在運行的Service取得聯系,那么有兩種方法,一種是使用廣播,另外一種方法就是使用bindService來建立聯系,前者的缺點是如果交流較為頻繁,容易造成性能上的問題,并且BroadcastReceiver本身執行代碼的時間是很短的(也許執行到一半,后面的代碼便不會執行),而后者則沒有這些問題,因此我們肯定選擇使用bindService(這個時候你便同時在使用startService和bindService了,這在Activity中更新Service的某些運行狀態是相當有用的)。
-
當服務被停止時
當一個服務被終止(stopService
、stopSelf
、unbindService
)時,onDestory方法將會被調用——所以我們需要在該方法中清除一些工作(依附該Service生命周期的,如:停止在Service中創建并運行的線程)。
特別注意:
1.在使用startService
方法啟動服務后,一定要調用stopService
方法來停止該服務(同上,可以在Activity的onDestory中來停止服務);
2.在某處調用bindService
綁定Service的時候,要在對應的某處調用unbindService
來解除綁定(如在Activity中綁定了Service,可以在onDestory中來解除綁定——雖然綁定的Service會在Activity結束時自動解除、停止);
3.如果同時使用startService
與bindService
方法啟動Service,需要終止該Service時,要調用stopService
和unbindService
方法(unbindService
依附于啟動它的Context,startServicec
并不依附于啟動它的Context。如果先調用unbindService
,這時服務并不會被終止,當調用stopService
后,服務才會被終止;如果先調用stopService
,服務也不會被終止,當調用unbindService
或者之前調用bindService
的Context不存在了(如Activity被finish掉了)服務才會自動停止);
4.當手機屏幕發生旋轉時,如果Activity設置的是自動旋轉的話,在旋轉的過程中,Activity會重新創建,那么之前通過bindService
建立的連接便會斷開(之前的Context不存在了),服務也會被自動停止。
4.Service的使用
在新建一個Service后,記得在AndroidManifest.xml中注冊Service,在application內添加需要注冊的Service信息:
<service android:name=".service.PlayerService"
android:label="PlayerService"
android:exported="true" />
Service示例如下:
public class PlayerService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
/**
* onBind 是 Service 的虛方法,因此我們不得不實現它。
* 返回 null,表示客服端不能建立到此服務的連接。
*/
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
// 已取代onStart方法--onStart方法是在Android2.0之前的平臺使用的.
// 在2.0及其之后,則需重寫onStartCommand方法,同時,舊的onStart方法則不會再被直接調用
// (外部調用onStartCommand,而onStartCommand里會再調用 onStart。在2.0之后,
// 推薦覆蓋onStartCommand方法,而為了向前兼容,在onStartCommand依然會調用onStart方法。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
// IBinder是遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分。但它不僅用于遠程
// 調用,也用于進程內調用。這個接口定義了與遠程對象交互的協議。
// 不要直接實現這個接口,而應該從Binder派生。
// Binder類已實現了IBinder接口
class MyBinder extends Binder {
/**
* 獲取Service的方法
* @return 返回PlayerService
*/
public PlayerService getService(){
return PlayerService.this;
}
}
}
-
startService啟動服務
通過startService()
啟動的服務處于“啟動”狀態,一旦啟動,Service就在后臺運行,即使啟動它的應用組件已經被銷毀了。通常started狀態的Service執行單任務并且不返回任何結果給啟動者(如當下載或上傳一個文件,當這項操作完成時,Service應該停止它本身),示例如下:
public class PlayerActivity extends Activity{
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, PlayerService.class);
startService(intent);// 啟動服務
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(intent);// 在退出Activity時停止該服務
}
}
-
bindService綁定服務
一個綁定的Service提供一個允許組件與Service交互的接口,可以發送請求、獲取返回結果,還可以通過跨進程通信來交互(IPC)。綁定的Service只有當應用組件綁定后才能運行,多個組件可以綁定一個Service,當調用unbindService()
方法時,這個Service就會被銷毀了。 - Local(本地服務)的綁定
public class PlayerActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, PlayerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);//綁定目標Service
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);// 解除綁定,斷開連接
}
// 在Activity中,我們通過ServiceConnection接口來取得建立連接與連接意外丟失的回調
ServiceConnection serviceConnection = new ServiceConnection() { @Override
public void onServiceConnected(ComponentName name, IBinder service){
// 建立連接
// 獲取服務的操作對象
PlayerService.MyBinder binder = (PlayerService.MyBinder)service;
binder.getService();// 獲取到的Service即PlayerService
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 連接斷開
}
};
}
注
Service的onBind
如果返回null,則調用bindService
會啟動Service,但不會連接上Service因此ServiceConnection.onServiceConnected不會被調用,但仍然需要使用unbindService
方法來斷開連接,這樣Service才會停止。
-
Remote(遠程服務)的綁定
【Android】遠程服務(Remote Service)的使用 -
前臺服務的使用
請訪問【Android】Service前臺服務的使用
注
Service與Activity一樣都存在與當前進程的主線程中,所以,一些阻塞UI的操作(如在Service進行網絡請求等)不能放在Service里進行。如果在Service里進行一些耗CPU和耗時操作,可能會引發ANR警告,這時應用會彈出是強制關閉還是等待的對話框。所以,對Service的理解就是和Activity平級的,只不過是看不見的,在后臺運行的一個組件,這也是為什么和Activity同被說為Android的基本組件。
5. AndroidManifest.xml中Service元素常見屬性
- andorid:name
服務類名。可以是完整的包名+類名。也可使用
.
代替包名。
- adroid:exported
其他應用能否訪問該服務,如果不能,則只有本應用或有相同用戶ID的應用能訪問。默認為false。
- android:enabled
標識服務是否可以被系統實例化。true--系統默認啟動,false--不啟動。(默認值為true)
- android:label
顯示給用戶的服務名稱。如果沒有進行服務名稱的設置,默認顯示服務的類名。
- android:process
服務所運行的進程名。默認是在當前進程下運行,與包名一致。如果進行了設置,將會在包名后加上設置的集成名。
如果名稱設置為冒號:
開頭,一個對應用程序私有的新進程會在需要時和運行到這個進程時建立。如果名稱為小寫字母開頭,服務會在一個相同名字的全局進程運行,如果有權限這樣的話。這允許不同應用程序的組件可以分享一個進程,減少了資源的使用。
- android:icon
服務的圖標。
- android:permission
申請使用該服務的權限,如果沒有配置下相關權限,服務將不執行,使用
startService()
、bindService()
方法將都得不到執行。
6.Thread與Service的區別
- Thread的優先級低于Service的優先級,在系統資源緊張的情況下,優先殺死前者;
- 在Thread啟動后,如果退出當前Activity,則無法對已經啟動的Thread進行操作,而服務則可以——因此可以在服務中啟動Thread來解決該問題;
- Service可以進行跨進程訪問,而Thread不行;
- 在應用中,如果是長時間的在后臺運行,而且不需要交互的情況下,使用Service;
- 同樣是在后臺運行,不需要交互的情況下,如果只是完成某個任務,之后就不需要運行,而且可能是多個任務,需需要長時間運行的情況下使用Thread;
- 如果任務占用CPU時間多,資源大的情況下,要使用Thread。
-
拓展閱讀
Thread和Service應用場合的區別
Android中Service與Thread的區別
android-Service和Thread的區別