Service 是 Android 中實(shí)現(xiàn)程序后臺(tái)運(yùn)行的解決方案,它非常適用于去執(zhí)行那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)。Service 默認(rèn)并不會(huì)運(yùn)行在子線程中,它也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中,它同樣執(zhí)行在 UI 線程中,因此,不要在 Service 中執(zhí)行耗時(shí)的操作,除非你在 Service 中創(chuàng)建了子線程來完成耗時(shí)操作
Service 的運(yùn)行不依賴于任何用戶界面,即使程序被切換到后臺(tái)或者用戶打開另一個(gè)應(yīng)用程序,Service 仍然能夠保持正常運(yùn)行,這也正是 Service 的使用場(chǎng)景。當(dāng)某個(gè)應(yīng)用程序進(jìn)程被殺掉時(shí),所有依賴于該進(jìn)程的 Service 也會(huì)停止運(yùn)行。
Service的生命周期

通過這個(gè)圖可以看到,兩種啟動(dòng) Service 的方式以及他們的生命周期, bindService 的不同之處在于當(dāng)綁定的組件銷毀后,對(duì)應(yīng)的 Service 也就被 Kill 了。
-
被啟動(dòng)的服務(wù)的生命周期
一個(gè) Service 被使用 startService 方法啟動(dòng),不管是否調(diào)用了 bindService(綁定服務(wù))或 unbindService (解除綁定服務(wù))到該 Service ,該 Service 都會(huì)在后臺(tái)運(yùn)行并不受影響。
一個(gè) Service 被使用 startService 方法啟動(dòng)多少次,onCreate 方法只會(huì)調(diào)用一次,onStartCommand 方法將會(huì)被調(diào)用多次(與startService的次數(shù)一致),且系統(tǒng)只會(huì)創(chuàng)建一個(gè) Service 實(shí)例(結(jié)束該 Service 也只需要調(diào)用一次 stopService ),該 Service 會(huì)一直在后臺(tái)運(yùn)行,直至調(diào)用 stopService 或調(diào)用自身的 stopSelf 方法。
注:在系統(tǒng)資源不足的情況下,服務(wù)有可能被系統(tǒng)結(jié)束(Kill);
-
被綁定的服務(wù)的生命周期
如果一個(gè) Service 在某個(gè) Activity 中被調(diào)用 bindService 方法啟動(dòng),不論 bindService 被調(diào)用幾次, Service 的 onCreate 方法只會(huì)執(zhí)行一次,同時(shí) onStartCommand 方法始終不會(huì)調(diào)用。
當(dāng)建立連接后,Service會(huì)一直運(yùn)行,除非調(diào)用 unbindService 來接觸綁定、斷開連接或調(diào)用該 Service 的 Context 不存在了(如 Activity 被 Finish ——即通過 bindService 啟動(dòng)的 Service 的生命周期依附于啟動(dòng)它的 Context ),系統(tǒng)在這時(shí)會(huì)自動(dòng)停止該 -
被啟動(dòng)又被綁定的服務(wù)的生命周期
當(dāng)一個(gè) Service 在被啟動(dòng) (startService) 的同時(shí)又被綁定 (bindService) ,該 Service 將會(huì)一直在后臺(tái)運(yùn)行,并且不管調(diào)用幾次, onCreate 方法始終只會(huì)調(diào)用一次, onStartCommand 的調(diào)用次數(shù)與 startService 調(diào)用的次數(shù)一致(使用 bindService 方法不會(huì)調(diào)用 onStartCommand )。同時(shí),調(diào)用 unBindService 將不會(huì)停止 Service ,必須調(diào)用 stopService 或 Service 自身的 stopSelf 來停止服務(wù)。
-
當(dāng)服務(wù)被停止時(shí)
當(dāng)一個(gè)服務(wù)被終止(stopService、stopSelf、unbindService)時(shí),onDestory 方法將會(huì)被調(diào)用——所以我們需要在該方法中清除一些工作(依附該 Service 生命周期的,如:停止在 Service 中創(chuàng)建并運(yùn)行的線程)。
特別注意:
- 在使用 startService 方法啟動(dòng)服務(wù)后,一定要調(diào)用 stopService 方法來停止該服務(wù)(同上,可以在 Activity 的onDestory 中來停止服務(wù));
- 在某處調(diào)用 bindService 綁定 Service 的時(shí)候,要在對(duì)應(yīng)的某處調(diào)用 unbindService 來解除綁定(如在 Activity 中綁定了 Service ,可以在 onDestory 中來解除綁定——雖然綁定的 Service 會(huì)在 Activity 結(jié)束時(shí)自動(dòng)解除、停止);
- 如果同時(shí)使用 startService 與 bindService 方法啟動(dòng) Service ,需要終止該 Service 時(shí),要調(diào)用 stopService 和 unbindService 方法( unbindService 依附于啟動(dòng)它的 Context ,startServicec 并不依附于啟動(dòng)它的 Context 。如果先調(diào)用 unbindService ,這時(shí)服務(wù)并不會(huì)被終止,當(dāng)調(diào)用 stopService 后,服務(wù)才會(huì)被終止;如果先調(diào)用 stopService ,服務(wù)也不會(huì)被終止,當(dāng)調(diào)用 unbindService 或者之前調(diào)用 bindService 的 Context 不存在了(如 Activity 被 finish 掉了)服務(wù)才會(huì)自動(dòng)停止);
- 當(dāng)手機(jī)屏幕發(fā)生旋轉(zhuǎn)時(shí),如果 Activity 設(shè)置的是自動(dòng)旋轉(zhuǎn)的話,在旋轉(zhuǎn)的過程中, Activity 會(huì)重新創(chuàng)建,那么之前通過 bindService 建立的連接便會(huì)斷開(之前綁定該服務(wù)的 Context 不存在了),服務(wù)也會(huì)被自動(dòng)停止。
Service 使用
在新建一個(gè) Service 后,記得在 AndroidManifest.xml 中注冊(cè) Service ,在 application 內(nèi)添加需要注冊(cè)的 Service 信息:
<service
android:name=".service.PlayerService"
android:label="PlayerService"
android:exported="true" />
AndroidManifest.xml中Service元素常見屬性:
-
andorid:name
服務(wù)類名??梢允峭暾陌?+ 類名。也可使用.代替包名。
-
adroid:exported
其他應(yīng)用能否訪問該服務(wù),如果不能,則只有本應(yīng)用或有相同用戶 ID 的應(yīng)用能訪問。默認(rèn)為 false 。
-
android:enabled
標(biāo)識(shí)服務(wù)是否可以被系統(tǒng)實(shí)例化。 true --系統(tǒng)默認(rèn)啟動(dòng), false --不啟動(dòng)。(默認(rèn)值為 true )
-
android:label
顯示給用戶的服務(wù)名稱。如果沒有進(jìn)行服務(wù)名稱的設(shè)置,默認(rèn)顯示服務(wù)的類名。
-
android:process
服務(wù)所運(yùn)行的進(jìn)程名。默認(rèn)是在當(dāng)前進(jìn)程下運(yùn)行,與包名一致。如果進(jìn)行了設(shè)置,將會(huì)在包名后加上設(shè)置的集成名。
如果名稱設(shè)置為冒號(hào) :開頭,一個(gè)對(duì)應(yīng)用程序私有的新進(jìn)程會(huì)在需要時(shí)和運(yùn)行到這個(gè)進(jìn)程時(shí)建立。如果名稱為小寫字母開頭,服務(wù)會(huì)在一個(gè)相同名字的全局進(jìn)程運(yùn)行,如果有權(quán)限這樣的話。這允許不同應(yīng)用程序的組件可以分享一個(gè)進(jìn)程,減少了資源的使用。 -
android:icon
服務(wù)的圖標(biāo)。
-
android:permission
申請(qǐng)使用該服務(wù)的權(quán)限,如果沒有配置下相關(guān)權(quán)限,服務(wù)將不執(zhí)行,使用 startService() 、 bindService() 方法將都得不到執(zhí)行。
Service 種類
服務(wù)是一個(gè)應(yīng)用程序組件,可以在后臺(tái)執(zhí)行長時(shí)間運(yùn)行的操作,不提供用戶界面。一個(gè)應(yīng)用程序組件可以啟動(dòng)一個(gè)服務(wù),它將繼續(xù)在后臺(tái)運(yùn)行,即使用戶切換到另一個(gè)應(yīng)用程序。此外,一個(gè)組件可以綁定到一個(gè)服務(wù)與它交互,甚至執(zhí)行進(jìn)程間通信 (IPC) 。例如,一個(gè)服務(wù)可能處理網(wǎng)絡(luò)通信、播放音樂、計(jì)時(shí)操作或與一個(gè)內(nèi)容提供者交互,都在后臺(tái)執(zhí)行??偣部煞譃?strong>后臺(tái)服務(wù)、前臺(tái)服務(wù)、IntentService、跨進(jìn)程服務(wù)(遠(yuǎn)程服務(wù))、無障礙服務(wù)、**系統(tǒng)服務(wù) **。
- 后臺(tái)服務(wù)
后臺(tái)服務(wù)可交互性主要是體現(xiàn)在不同的啟動(dòng)服務(wù)方式, startService() 和 bindService() 。 bindService() 可以返回一個(gè)代理對(duì)象,可調(diào)用 Service 中的方法和獲取返回結(jié)果等操作,而 startService() 不行。
-
不可交互的后臺(tái)服務(wù)
不可交互的后臺(tái)服務(wù)即是普通的 Service , Service 的生命周期很簡單,分別為 onCreate、onStartCommand、onDestroy 這三個(gè)。當(dāng)我們 startService() 的時(shí)候,首次創(chuàng)建 Service 會(huì)回調(diào) onCreate() 方法,然后回調(diào) onStartCommand() 方法,再次 startService() 的時(shí)候,就只會(huì)執(zhí)行一次 onStartCommand() 。服務(wù)一旦開啟后,我們就需要通過 stopService() 方法或者 stopSelf() 方法,就能把服務(wù)關(guān)閉,這時(shí)就會(huì)回調(diào) onDestroy() 。
-
可交互的后臺(tái)服務(wù)
可交互的后臺(tái)服務(wù)是指前臺(tái)頁面可以調(diào)用后臺(tái)服務(wù)的方法,可交互的后臺(tái)服務(wù)實(shí)現(xiàn)步驟是和不可交互的后臺(tái)服務(wù)實(shí)現(xiàn)步驟是一樣的,區(qū)別在于啟動(dòng)的方式和獲得 Service 的代理對(duì)象。區(qū)別在于多了一個(gè) ServiceConnection 對(duì)象,該對(duì)象是用戶綁定后臺(tái)服務(wù)后,可獲取后臺(tái)服務(wù)代理對(duì)象的回調(diào),我們可以通過該回調(diào),拿到后臺(tái)服務(wù)的代理對(duì)象,并調(diào)用后臺(tái)服務(wù)定義的方法,也就實(shí)現(xiàn)了后臺(tái)服務(wù)和前臺(tái)的交互。
- 前臺(tái)服務(wù)
由于后臺(tái)服務(wù)優(yōu)先級(jí)相對(duì)比較低,當(dāng)系統(tǒng)出現(xiàn)內(nèi)存不足的情況下,它就有可能會(huì)被回收掉,所以前臺(tái)服務(wù)就是來彌補(bǔ)這個(gè)缺點(diǎn)的,它可以一直保持運(yùn)行狀態(tài)而不被系統(tǒng)回收。例如:墨跡天氣在狀態(tài)欄中的天氣預(yù)報(bào)。
前臺(tái)服務(wù)創(chuàng)建很簡單,其實(shí)就在 Service 的基礎(chǔ)上創(chuàng)建一個(gè) Notification ,然后使用 Service 的 startForeground() 方法即可啟動(dòng)為前臺(tái)服務(wù)
startService(new Intent(this, ForegroundService.class));
- IntentService
IntentService 是專門用來解決 Service 中不能執(zhí)行耗時(shí)操作這一問題的,創(chuàng)建一個(gè) IntentService 也很簡單,只要繼承 IntentService 并覆寫 onHandlerIntent 函數(shù),在該函數(shù)中就可以執(zhí)行耗時(shí)操作了。
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
// 在這里執(zhí)行耗時(shí)操作
}
}
在 IntentService 內(nèi)有一個(gè)工作線程來處理耗時(shí)操作,當(dāng)任務(wù)執(zhí)行完后,IntentService 會(huì)自動(dòng)停止,不需要我們?nèi)ナ謩?dòng)結(jié)束。如果啟動(dòng) IntentService 多次,那么每一個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在 IntentService 的 onHandleIntent 回調(diào)方法中執(zhí)行,依次去執(zhí)行,執(zhí)行完自動(dòng)結(jié)束。
- 跨進(jìn)程服務(wù)(遠(yuǎn)程服務(wù))
遠(yuǎn)程服務(wù)為獨(dú)立的進(jìn)程,對(duì)應(yīng)進(jìn)程名格式為所在包名加上你指定的 android:process 字符串。由于是獨(dú)立的進(jìn)程,因此在 Activity 所在進(jìn)程被 Kill 的時(shí)候,該服務(wù)依然在運(yùn)行,不受其他進(jìn)程影響,有利于為多個(gè)進(jìn)程提供服務(wù)具有較高的靈活性。由于是獨(dú)立的進(jìn)程,會(huì)占用一定資源,并且使用 AIDL 進(jìn)行 IPC 稍微麻煩一點(diǎn),這種 Service 是常駐的。
> 關(guān)于 AIDL 推薦大家看看[Android使用AIDL實(shí)現(xiàn)跨進(jìn)程通訊(IPC)](http://blog.csdn.net/ydxlt/article/details/50812559)
-
AccessibilityService無障礙服務(wù)
無障礙服務(wù)旨在幫助身心有障礙的用戶使用 Android 設(shè)備和應(yīng)用。無障礙服務(wù)在后臺(tái)運(yùn)行,當(dāng)無障礙事件被激活時(shí)系統(tǒng)會(huì)執(zhí)行 AccessibilityService 的 onAccessibilityEvent(AccessibilityEvent event) 方法。這些事件表示在用戶界面中的一些狀態(tài)的改變,例如:焦點(diǎn)的改變、按鈕被點(diǎn)擊等。這類服務(wù)可以有選擇性地請(qǐng)求查詢活動(dòng)窗口的內(nèi)容。無障礙服務(wù)的開發(fā)需要繼承 AccessibilityService 和實(shí)現(xiàn)它的抽象方法。
跟詳細(xì)的介紹推薦閱讀 Android 無障礙輔助功能AccessibilityService(1) -
系統(tǒng)服務(wù)
系統(tǒng)服務(wù)提供了很多便捷服務(wù),可以查詢 Wifi 、網(wǎng)絡(luò)狀態(tài)、查詢電量、查詢音量、查詢包名、查詢 Application 信息等等等相關(guān)多的服務(wù),具體大家可以自信查詢文檔,這里舉例幾個(gè)常見的服務(wù):
-
判斷Wifi是否開啟
WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE); boolean enabled = wm.isWifiEnabled(); -
獲取系統(tǒng)最大音量
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM); -
獲取當(dāng)前音量
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); int current = am.getStreamMaxVolume(AudioManager.STREAM_RING); -
判斷網(wǎng)絡(luò)是否有連接
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); boolean isAvailable = info.isAvailable();
-