Android Service最全面的解析
Service
是Android
中一個類,它是Android四大元件之一,使用Service
可以在後臺執行長時間的操作( perform long-running operations in the background
),Service
並不與使用者產生UI互動。其他的應用元件可以啟動Service
,即便使用者切換了其他應用,啟動的Service
仍可在後臺執行。一個元件可以與Service
繫結並與之互動,甚至是跨程式通訊(IPC
)。例如,一個Service
可以在後臺執行網路請求、播放音樂、執行檔案讀寫操作或者與 content provider
互動 等。
本文將介紹Services
的定義、建立、啟動、繫結、前臺Service等相關內容,如需訪問官方原文,您可以點選這個連結:《Services》
Services
Services
有兩種啟動形式:
Started
:其他元件呼叫startService()
方法啟動一個Service
。一旦啟動,Service
將一直執行在後臺(run in the background indefinitely
)即便啟動Service
的元件已被destroy
。通常,一個被start的Service
會在後臺執行單獨的操作,也並不給啟動它的元件返回結果。比如說,一個start
的Service
執行在後臺下載或上傳一個檔案的操作,完成之後,Service
應自己停止。Bound
:其他元件呼叫bindService()
方法繫結一個Service
。通過繫結方式啟動的Service
是一個client-server
結構,該Service
可以與繫結它的元件進行互動。一個bound service
僅在有元件與其繫結時才會執行(A bound service runs only as long as another application component is bound to it
),多個元件可與一個service
繫結,service
不再與任何元件繫結時,該service
會被destroy
。
當然,service
也可以同時在上述兩種方式下執行。這涉及到Service
中兩個回撥方法的執行:onStartCommand()
(通過start
方式啟動一個service
時回撥的方法)、onBind()
(通過bind方式啟動一個service
回撥的方法)。
無論通過那種方式啟動service
(start、bind、start&bind),任何元件(甚至其他應用的元件)都可以使用service
。並通過Intent
傳遞引數。當然,您也可以將Service
在manifest
檔案中配置成私有的,不允許其他應用訪問。
!請注意:Service
執行在主執行緒中(A service runs in the main thread of its hosting process
),Service
並不是一個新的執行緒,也不是新的程式。也就是說,若您需要在Service
中執行較為耗時的操作(如播放音樂、執行網路請求等),需要在Service
中建立一個新的執行緒。這可以防止ANR
的發生,同時主執行緒可以執行正常的UI操作。
使用Service還是使用Thread?
Service
是一個執行在後臺的元件,並不與使用者互動。您僅在需要的時候建立Service
( create a service only if that is what you need
)。
當使用者正在與UI互動時,需要執行一些主執行緒無法完成的工作,應當建立一個執行緒。例如當activity
正在執行時,需要播放音樂,此時需要在Activity
的onCreate()
中建立執行緒並在onStart()
中開啟。最後在onStop()
中停止。您也可以考慮使用AsyncTask
或 HandlerThread
來替代Thread
建立執行緒。
Service
基礎(The Basics)
為了建立Service
,需要繼承Service
類。並重寫它的回撥方法,這些回撥方法反應了Service
的生命週期,並提供了繫結Service
的機制。最重要的Service
的生命週期回撥方法如下所示:
onStartCommand()
:當其他元件呼叫startService()
方法請求啟動Service
時,該方法被回撥。一旦Service
啟動,它會在後臺獨立執行。當Service
執行完以後,需呼叫stopSelf()
或stopService()
方法停止Service
。(若您只希望bind Service
,則無需呼叫這些方法)onBind()
:當其他元件呼叫bindService()
方法請求繫結Service
時,該方法被回撥。該方法返回一個IBinder
介面,該介面是Service
與繫結的元件進行互動的橋樑。若Service
未繫結其他元件,該方法應返回null
。onCreate()
:當Service
第一次建立時,回撥該方法。該方法只被回撥一次,並在onStartCommand()
或onBind()
方法被回撥之前執行。若Service
處於執行狀態,該方法不會回撥。onDestroy()
:當Service
被銷燬時回撥,在該方法中應清除一些佔用的資源,如停止執行緒、接觸繫結註冊的監聽器或broadcast receiver
等。該方法是Service
中的最後一個回撥。
如果某個元件通過呼叫startService()
啟動了Service
(系統會回撥onStartCommand()
方法),那麼直到在Service
中手動呼叫stopSelf()
方法、或在其他元件中手動呼叫stopService()
方法,該Service
才會停止。
如果某個元件通過呼叫bindService()
繫結了Service
(系統不會回撥onStartCommand()
方法),只要該元件與Service
處於繫結狀態,Service
就會一直執行,當Service
不再與元件繫結時,該Service
將被destroy
。
當系統記憶體低時,系統將強制停止Service
的執行;若Service
繫結了正在與使用者互動的activity
,那麼該Service
將不大可能被系統kill
( less likely to be killed
)。如果建立的是前臺Service
,那麼該Service
幾乎不會被kill
(almost never be killed
)。否則,當建立了一個長時間在後臺執行的Service
後,系統會降低該Service
在後臺任務棧中的級別——這意味著它容易被kill
(lower its position in the list of background tasks over time and the service will become highly susceptible to killing
),所以在開發Service
時,需要使Service
變得容易被restart
,因為一旦Service
被kill
,再restart
它需要其資源可用時才行(restarts it as soon as resources become available again
),當然這也取決於onStartCommand()
方法返回的值,這將在後續介紹。
在manifest
檔案中註冊service
(Declaring a service in the manifest
)
在manifest
檔案中註冊service
的方式如下:
除此之外,在<service>
標籤中還可以配置其他屬性,比如,需要啟動該service
所需的許可權、該service
應執行在哪個程式中 等( permissions required to start the service and the process in which the service should run
)。android:name
屬性是唯一不可預設的,它指定了Service
的全限定類名。一旦釋出了應用,該類名將不可更改。
!請注意:為了保證應用的安全,請使用顯式Intent啟動或繫結一個Service
,請不要在<service>
標籤中配置intent-filter
。
若不確定該啟動哪個Service
,那麼可以在<service>
中配置intent-filter
,並在Intent
中排除該Service
(supply intent filters for your services and exclude the component name from the Intent
),但必須呼叫Intent
的setPackage()
方法,來為啟動的service
消除歧義(provides sufficient disambiguation for the target service
)。
注:setPackage()
方法傳入一個String
引數,代表一個包名。該方法表示該Intent
物件只能在傳入的這個包名下尋找符合條件的元件,若傳入null
,則表示可以在任意包下尋找。
將android:exported
屬性設為false
,表示不允許其他應用程式啟動本應用的元件,即便是顯式Intent
也不行(even when using an explicit intent
)。這可以防止其他應用程式啟動您的service
元件。
使用start
方式啟動Service
(Creating a Started Service
)
其他元件呼叫startService()
方法可以啟動一個Service
,接著,Service
會回撥onStartCommand()
生命週期方法。startService()
方法中傳入一個Intent
引數,用於顯式指定目標Service
的名字,並攜帶data
以供Service
使用,該Intent
引數將回傳至onStartCommand()
方法中。
比如說,Activity
需要向線上資料庫中上傳資料,那麼可以呼叫startService()
啟動一個Service
,並將資料傳入Intent
的data
中,接著,onStartCommand()
方法會接收這個Intent
並開啟一個執行緒將資料上傳至網路,當資料上傳完成後,該Service
將停止並被destroy
。
一般使用如下兩種方式建立一個start Service
:
-
繼承
Service
類:請務必在Service
中開啟執行緒來執行耗時操作,因為Service
執行在主執行緒中。 -
繼承
IntentService
類:IntentService
繼承於Service
,若Service
不需要同時處理多個請求,那麼使用IntentService
將是最好選擇:您只需要重寫onHandleIntent()
方法,該方法接收一個回傳的Intent
引數,您可以在方法內進行耗時操作,因為它預設開啟了一個子執行緒,操作執行完成後也無需手動呼叫stopSelf()
方法,onHandleIntent()
會自動呼叫該方法。
繼承IntentService
類(Extending the IntentService class
)
在大多數情況下,start Service
並不會同時處理多個請求(don’t need to handle multiple requests simultaneously
),因為處理多執行緒較為危險(a dangerous multi-threading scenario
),所以繼承IntentService
類帶建立Service
是個不錯選擇。
使用IntentService
的要點如下:
- 預設在子執行緒中處理回傳到
onStartCommand()
方法中的Intent
; - 在重寫的
onHandleIntent()
方法中處理按時間排序的Intent
佇列,所以不用擔心多執行緒(multi-threading
)帶來的問題。 - 當所有請求處理完成後,自動停止
service
,無需手動呼叫stopSelf()
方法; - 預設實現了
onBind()
方法,並返回null
; - 預設實現了
onStartCommand()
方法,並將回傳的Intent
以序列的形式傳送給onHandleIntent()
,您只需重寫該方法並處理Intent
即可。
綜上所述,您只需重寫onHandleIntent()
方法即可,當然,還需要建立一個構造方法,示例如下:
如果您還希望在IntentService
的 繼承類中重寫其他生命週期方法,如onCreate()
、onStartCommand()
或 onDestroy()
,那麼請先呼叫各自的父類方法以保證子執行緒能夠正常啟動。
比如,要實現onStartCommand()
方法,需返回其父類方法:
除onHandleIntent()
外,onBind()
方法也無需呼叫其父類方法。
繼承Service
類(Extending the Service class
)
如果您需要在Service
中執行多執行緒而不是處理一個請求佇列(perform multi-threading instead of processing start requests through a work queue
),那麼需要繼承Service
類,分別處理每個Intent
。
在Service
中執行操作時,處理每個請求都需要開啟一個執行緒,並且同一時刻一個執行緒只能處理一個請求( for each start request, it uses a worker thread to perform the job and processes only one request at a time
)。
注意到onStartCommand()
返回一個整形變數,該變數必須是下列常量之一:
START_NOT_STICKY
:若執行完onStartCommand()
方法後,系統就kill了service
,不要再重新建立service
,除非系統回傳了一個pending intent
。這避免了在不必要的時候執行service
,您的應用也可以restart
任何未完成的操作。START_STICKY
:若系統在onStartCommand()
執行並返回後kill
了service
,那麼service
會被recreate
並回撥onStartCommand()
。dangerous
不要重新傳遞最後一個Intent
(do not redeliver the last intent
)。相反,系統回撥onStartCommand()
時回傳一個空的Intent
,除非有pending intents
傳遞,否則Intent
將為null
。該模式適合做一些類似播放音樂的操作。START_REDELIVER_INTENT
:若系統在onStartCommand()
執行並返回後kill
了service
,那麼service
會被recreate
並回撥onStartCommand()
並將最後一個Intent
回傳至該方法。任何pending intents
都會被輪流傳遞。該模式適合做一些類似下載檔案的操作。
啟動服務(Starting a Service)
若需要啟動Service
,見下面所示:
startService(intent)
方法將立即返回,並回撥onStartCommand()
(請不要手動呼叫該方法),若該Service
未處於執行狀態,系統將首先回撥onCreate()
,接著再回撥onStartCommand()
。若您希望Service
可以返回結果,那麼需要通過呼叫getBroadcast
返回的PendingIntent
啟動Service
(將PendingIntent
包裝為Intent
),service
可使用broadcast
傳遞結果。
多個啟動Service
的請求可能導致onStartCommand()
多次呼叫,但只需呼叫stopSelf()
、stopService()
這兩個方法之一,就可停止該服務。
停止服務(Stopping a service)
一個啟動的Service
必須管理自己的生命週期。系統不會主動stop
或destroy
一個執行的Service
,除非系統記憶體緊張,否則,執行完onStartCommand()
方法後,Service
依然執行。停止Service
必須手動呼叫stopSelf()
(在Service
中)或呼叫stopService()
(在啟動元件中)。
一旦呼叫了上述兩種方法之一,系統會盡快destroy
該Service
(as soon as possible
)。
若系統正在處理多個呼叫onStartCommand()
請求,那麼在啟動一個請求時,您不應當在此時停止該Service
(you shouldn’t stop the service when you’re done processing a start request
)。為了避免這個問題,您可以呼叫stopSelf(int)
方法,以確保請求停止的Service
時最新的啟動請求( your request to stop the service is always based on the most recent start request
)。這就是說,當呼叫stopSelf(int)
方法時,傳入的ID代表啟動請求(該ID會傳遞至onStartCommand()
),該ID與請求停止的ID一致。則如果在呼叫stopSelf(int)
之前,Service
收到一個新的Start
請求,ID將無法匹配,Service
並不會停止。
為了節省記憶體和電量,當Service
完成其工作後將其stop
很有必要。如有必要,可以在其他元件中呼叫stopService()
方法,即便Service
處於繫結狀態,只要它回撥過onStartCommand()
,也應當主動停止該Service
。
建立繫結Service(Creating a Bound Service)
通過其他元件呼叫bindService()
方法可以繫結一個Service
以保持長連線(long-standing connection
),這時一般不允許其他元件呼叫startService()
啟動Service
。
當其他元件需要與Service
互動或者需要跨程式通訊時,可以建立一個bound Service
。
為建立一個bound Service
,必須重寫onBind()
回撥,該方法返回一個IBinder
介面。該介面時元件與Service
通訊的橋樑。元件呼叫bindService()
與Service
繫結,該元件可獲取IBinder
介面,一旦獲取該介面,就可以呼叫Service
中的方法。一旦沒有元件與Service
繫結,系統將destroy
它,您不必手動停止它。
為建立一個bound Service
,必須定義一個介面 ,該介面指定元件與Service
如何通訊。定義的介面在元件與Service
之間,且必須實現IBinder
介面。這正是onBind()
的返回值。一旦元件接收了IBinder
,元件與Service
便可以開始通訊。
多個元件可同時與Service
繫結,當元件與Service
互動結束後,可呼叫unbindService()
方法解綁。bound Service
比start Service
要複雜,故我將在後續單獨翻譯。
向使用者傳送通知(Sending Notifications to the User)
執行中的Service
可以通過Toast Notifications
或 Status Bar Notifications
向使用者傳送通知。Toast
是一個可以短時間彈出的提醒框。二Status Bar
是頂部狀態列中出現的太有圖示的資訊,使用者可以通過下拉狀態列獲得具體資訊並執行某些操作(如啟動Activity
)。
通常,Status Bar
用於通知某些操作已經完成,如下載檔案完成。當使用者下拉狀態列後,點選該通知,可獲取詳細內容,如檢視該下載的檔案。
執行前臺Service
(Running a Service in the Foreground
)
前臺Service
用於動態通知訊息,如天氣預報。該Service
不易被kill
。前臺Service
必須提供status bar
,只有前臺Service
被destroy
後,status bar
才能消失。
舉例來說,一個播放音樂的Service
必須是前臺Service
,只有這樣使用者才能確知其執行狀態。為前臺Service
提供的status bar
可以顯示當前音樂的播放狀態,並可以啟動播放音樂的Activity
。
呼叫startForeground()
可以啟動前臺Service
。該方法接收兩個引數,引數一是一個int
型變數,使用者指定該通知的唯一性標識,而引數而是一個Notification
用於配置status bar
,示例如下:
!注意:為startForeground()
設定的ID必須是0。
呼叫stopForeground()
來移除(remove
)前臺Service
。該方法需傳入一個boolean
型變數,表示是否也一併清除status bar
上的notification
(indicating whether to remove the status bar notification as well
)。該方法並不停止Service
,如果停止正在前臺執行的Service
,那麼notification
也一併被清除。
Service
生命週期(Managing the Lifecycle of a Service
)
從Service
的啟動到銷燬,有兩種路徑:
- A started service:需手動停止
- A bound service:可自動停止
如下圖所示 :
這兩條路徑並不是毫不相干的:當呼叫startService()
start
一個Service
後,您仍可以bind
該Service
。比如,當播放音樂時,需呼叫startService()
啟動指定播放的音樂,當需要獲取該音樂的播放進度時,有需要呼叫bindService()
,在這種情況下,知道Service
被unbind
,呼叫stopService()
或stopSelf()
都不能停止該Service
。
實現Service
的生命週期回撥(Implementing the lifecycle callbacks
)
這些生命週期方法在使用時無需呼叫各自的父類方法。
在兩條生命週期路徑中,都包含了兩個巢狀的生命週期:
- 完整生命週期(
entire lifetime
):從onCreate()
被呼叫到onDestroy()
返回。與Activity
類似,一般在onCreate()
中做一些初始化工作,而在onDestroy()
做一些資源釋放工作。如,若Service
在後臺播放一個音樂,就需要在onCreate()
方法中開啟一個執行緒啟動音樂,並在onDestroy()
中結束執行緒。-
無論是startService()
還是 bindService()
啟動Service
,onCreate()
和 onDestroy()
均會被回撥。
- 活動生命週期(
active lifetime
):從onStartCommand()
或onBind()
回撥開始。由相應的startService()
或bindService()
呼叫。
若是Start Service
,那麼Service
的活動生命週期結束就意味著其完整生命週期結束 (the active lifetime ends the same time that the entire lifetime ends
),即便onStartCommand()
返回後,Service
仍處於活動狀態;若是bound Service
,那麼當onUnbind()
返回時,Service
的活動生命週期結束。
!請注意:針對Start Service
,由於Service
中沒有類似onStop()
的回撥,所以在呼叫stopSelf()
或stopService()
後,只有onDestroy()
被回撥標誌著Service
已停止。
相關文章
- Android Service完全解析Android
- Rxjava2最全面的解析RxJava
- Android Service生命週期 Service裡面的onStartCommand()方法詳解Android
- Android 螢幕適配:最全面的解決方案Android
- Android Service重啟恢復(Service程式重啟)原理解析Android
- 最全面的UML教程
- Android ServiceAndroid
- 最強最全面的Hive SQL開發指南,超四萬字全面解析HiveSQL
- Android Intent ServiceAndroidIntent
- Android Service SecurityAndroid
- 最全面的Navigation的使用指南Navigation
- 最全面的 Bug 生命週期管理
- Android總結篇系列:Android ServiceAndroid
- uni-app&H5&Android混合開發一 || 最全面的uni-app離線打包Android平臺教程APPH5Android
- 史上最全面的React-react基礎React
- 最全面的86五筆字根表
- Android Service詳解(一)Android
- Android Service詳解(二)Android
- Android中Service總結Android
- Android中的Web ServiceAndroidWeb
- Android 之Service使用攻略Android
- Android-Service詳解Android
- Android之Service設定android:process作用Android
- 史上最全面的Spring Boot Cache使用與整合Spring Boot
- Android最全開發資源Android
- Android史上最全面試題Android面試題
- Android 面試2018最新最全Android面試
- 最全面的vue開源專案庫彙總Vue
- 可能是最全面的 python 字串拼接總結Python字串
- 可能是最全面的python字串拼接總結Python字串
- Linux流量監控工具 - iftop (最全面的iftop教程)Linux
- Mac全球最全面的取樣器:Native Instruments MaschineMac
- SpringBoot的外部化配置最全解析!Spring Boot
- MySQL欄位型別最全解析MySql型別
- Android Service生命週期淺析Android
- 最全面的CQRS和事件溯源介紹 - Software House ASC事件
- 最強最全面的數倉建設規範指南
- Java中的單例模式最全解析Java單例模式