Service 是 Android 的四大組件之一,它主要的做用是後臺執行操做,Activity 屬於帶有 UI 界面跟用戶進行交互,而 Service 則沒有 UI 界面,全部的操做都是基於後臺運行完成。而且 Service 跟 Activity 同樣也是能夠由其它的應用程序調用啓動的,並且就算用戶切換了應用程序,Service 依舊保持運行。一個組件若是與 Service 進行了綁定( bind ),就能夠跟 Service 進行數據的交互,而且也能夠跟不一樣的進程之間進行交互 (IPC)。一般會使用到 Service 的狀況有進行網絡請求,音樂的操控,文件的 I/O 操做等。html
Service 一般是經過如下兩種方式進行啓動java
當組件(例如 activity)經過調用 startService()
來啓動 Service 的時候。一旦啓動後,Service 就會獨立的在後臺運行,即便調用的組件已經銷燬了,Service 仍是能夠繼續在後臺運行。通常狀況下,只須要進行一次單獨的操做,不須要將操做後的結果返回給調用者的時候,會使用該方式啓動 Service。例如,進行上傳或者下載操做的時候,當操做完成後,Service 應該自行調用 stopService()
或 stopSelf()
來結束運行。android
當組件(例如 activity)經過調用 bindService()
來啓動 Service 的時候。這種方式提供了 client - service 的接口,可讓調用組件跟 Service 進行發送請求及返回結果的操做,設置能夠進行進程間的通訊 (IPC)。只要有一個組件對該 Service 進行了綁定,那該 Service 就不會銷燬。而且多個組件能夠同時對一個 Service 進行綁定,只有在全部進行了綁定的組件都解綁的時候,Service 纔會銷燬。git
儘管兩種方式是分開討論的,可是並非互斥的關係,使用 startService
啓動了 Service 後,也是能夠進行綁定的。github
注意: 雖然 Service 是在後臺運行,可是其實仍是在主線程裏進行全部的操做的。Service 在啓動時除非單獨進行了定義不然並無在單獨的線程或者進程了而都是在主線程裏。因此這表示任何能堵塞主線程的操做(例如音樂的播放或者網絡請求)都應該單獨開闢新的線程來進行操做,不然很容易出現 ANR 。api
在建立一個 Service 時,必需要去繼承 Service,而且要重寫父類一些主要的方法來實現功能。如下是主要方法的介紹安全
系統會調用這個函數當某個組件(例如 activity,fragment)經過調用 startService()
啓動 Service 時。在該方法被調用後,Service 就會被啓動並獨立的在後臺運行。若是重寫了該方法,開發者須要在 Service 執行完操做後自行的調用 stopSelf()
或 stopService()
,來結束 Service。若是隻是會經過綁定的方式 (bind) 的方式來啓動 Service 則不須要重寫該方法。網絡
系統會調用這個函數當某個組件(例如 activity, fragment)經過調用 bindService()
綁定的方式來啓動 Service 的時候。在實現這個函數的時候,必需要返回一個 IBinder 的繼承類,來與 Service 進行通訊。這個函數是默認必需要重寫的,可是若是不想經過綁定的方式來啓動 Service,則能夠直接返回 null
多線程
系統會調用此方法在第一次啓動 Service 的時候,用於初始化一些一次性的變量。若是 Service 已經啓動了,則此方法就不會再別調用。app
系統在 Service 已經不須要準備被銷燬的時候會調用此方法。Service 中若有用到 thread
、listeners
、receivers
等的時候,應該將這些的清理方法寫在此方法內。
以上就是實現一個 Service 中要實現的一些方法。
若是某個組件是經過調用 startService()
的方式來啓動了 Service,那這個 Service 就會一直在後臺運行直到 Service 內部調用 stopSelf()
或某個組件調用 stopService()
來結束該 Service。
若是某個組件是經過調用 bindService()
的方式來啓動了 Service,那這個 Service 就會一直在後臺運行直到該組件與其解綁。Service 在沒有任何組件綁定的時候,系統會將其銷燬
下面的環節,將介紹若是經過上面講述的兩種方式來建立 Service
相似於 Activity
,全部的 Service 都要在 Manifest 裏面進行聲明,以下:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>複製代碼
經過在 <service>
標籤裏將 android:exported
設置爲 false
。能夠防止其餘的程序來啓動你的 Service。
組件(例如 activity, fragment) 經過調用 startService()
方法,系統隨之調用 onStartCommand()
方法來實現 started
方式啓動 Service。
當 Service 以該形式啓動後,Service 的整個生命週期是徹底獨立的,即使啓動 Service 的組件已經被銷燬了,Service 仍是能夠在後臺無限的運行的。但開發者應該在 Service 中的操做執行完成後,調用 stopSelf()
或其它組件調用 stopService()
的方式來結束該 Service。
程序組件(例如 activity) 能夠經過傳遞一個 Intent 給 startService()
,來實現組件與 Service 以前的數據傳遞。Service 是經過系統調用的 onStartCommand()
方法接受傳遞的Intent
,完成整個數據傳遞過程。
注意: Service 自己默認是運行在主線程裏的,因此若是在 Service 要進行一些會堵塞線程的操做,必定要將這些操做放在一個新的線程裏。
Android 的框架提供了 IntentService 來知足後臺運行異步線程的需求。
IntentService 是 Service 的子類,而且全部的請求操做都是在異步線程裏。若是不須要 Service 來同時處理多個請求的話,IntentService 將會是最佳的選擇。只須要繼承並重寫 IntentService 中的 onHandleIntent()
方法,就能夠對接受到的 Intent
作後臺的異步線程操做了。
IntentService 提供了以下的幾個功能:
onStartCommand()
的 intents 。onHandleIntent()
方法中,避免了多線程問題。onBind()
方法的實現,返回 nullonStartCommand()
方法的實現,將收到的 intent 放到隊列池中,而後再交由 onHandleIntent()
作處理全部開發者只須要實現 onHandleIntent()
關注於 Service 要進行的操做便可。關於更多的 IntentService 實用技巧,請查看此文章。
若是開發者須要重寫其餘的一些方法,例如 onCreate(), onStartCommand(), 和 onDestroy(),請保證調用父類的實現,這樣能夠保證 IntentService 可以正確的來處理線程的生命週期。例如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}複製代碼
當系統內存不足的時候,系統會強制回收一些 Activity 和 Service 來獲取更多的資源給那些用戶正在交互的程序或頁面。這就要求 Service 可以自動重啓當資源充足的時候。這個功能是經過 onStartCommand()
的返回值來實現的
當系統因回收資源而銷燬了 Service,當資源再次充足時不自動啓動 Service。除非還有爲處理的 Intent 準備發送。當你的程序能夠很容易的從新開啓未完成的操做時,這是最安全的避免 Service 在沒必要要的狀況下啓動的選項。
當系統因回收資源而銷燬了 Service,當資源再次充足時自動啓動 Service,而且再次調用 onStartCommand()
方法,可是不會傳遞最後一次的 Intent,相反系統在回調onStartCommand()
的時候會傳一個空 Intent 。除非還有爲處理的 Intent 準備發送。
當系統因回收資源而銷燬了 Service,當資源再次充足時自動啓動 Service,而且再次調用 onStartCommand()
方法,並會把最後一次 Intent 再次傳遞給,onStartCommand()
,相應的在隊列裏的 Intent 也會按次序一次傳遞。此模式適用於下載等服務。
該方式容許多個組件同時對相同的 Service 進行 startService
操做,可是若是隻要有其中有一個組件調用了 stopSelf()
或 stopService()
, 該 Service 就會被銷燬。
Intent intent = new Intent(this, HelloService.class);
startService(intent);複製代碼
當有多個組件進行了 startService
操做時,不該該直接的去調用 stopSelf()
或 stopService()
來結束 Service, 由於這會對其餘已經發起請求的操做產生影響,故在 onStartCommand()
方法中會接受一個 startId
, 而後在結束 Service 時,調用 stopService(int)
方法來只是結束一個特定的請求,從而達到保護其餘請求不受影響的目的。
當應用程序中的 activity 或其它組件須要與服務進行交互,或者應用程序的某些功能須要暴露給其它應用程序時,你應該建立一個 Bind 服務,並經過進程間通訊(IPC)來完成。
Service 只在爲綁定的應用程序組件工做時纔會存活,所以,只要沒有組件綁定到服務,系統就會自動銷燬服務
若是服務是你的應用程序所私有的,而且與客戶端運行於同一個進程中(一般都是如此),你應該經過擴展 Binder 類來建立你的接口,並從 onBind()
返回一個它的實例。客戶端接收該 Binder
對象並用它來直接訪問 Binder
甚至 Service
中可用的公共方法。
若是你的服務只是爲你本身的應用程序執行一些後臺工做,那這就是首選的技術方案。不用這種方式來建立接口的理由只有一個,就是服務要被其它應用程序使用或者要跨多個進程使用。
若是你須要接口跨越多個進程進行工做,能夠經過 Messenger
來爲服務建立接口。在這種方式下,服務定義一個響應各種消息對象 Message
的 Handler
。此 Handler
是 Messenger
與客戶端共享同一個 IBinder
的基礎,它使得客戶端能夠用消息對象 Message
向服務發送指令。此外,客戶端還能夠定義本身的 Message
,以便服務可以往回發送消息。
這是執行進程間通訊(IPC)最爲簡便的方式,由於 Messenger
會把全部的請求放入一個獨立進程中的隊列,這樣你就不必定非要把服務設計爲線程安全的模式了。
絕大多數應用程序都不該該用 AIDL 來建立 bind 服務,由於這可能須要多線程處理能力而且會讓代碼變得更爲複雜。 所以,AIDL 對絕大多數應用程序都不適用,而且本文也不會討論如何在服務中使用它的內容。若是你確信須要直接使用 AIDL,那請參閱 AIDL 文檔。
若是你的服務只用於本地應用程序而且不須要跨進程工做,那你只要實現本身的 Binder 類便可,這樣你的客戶端就能直接訪問服務中的公共方法了。
注意:僅當客戶端和服務位於同一個應用程序和進程中,這也是最多見的狀況,這種方式纔會有用。好比,一個音樂應用須要把一個 activity 綁定到它本身的後臺音樂播放服務上,採用這種方式就會很不錯。
如下是設置步驟:
注意:
服務和客戶端之因此必須位於同一個應用程序中,是爲了讓客戶端可以正確轉換(cast)返回的對象並調用對象的 API。 服務和客戶端也必須位於同一個進程中,由於這種方式不能執行任何跨進程的序列化(marshalling)操做。
具體的實用案例請查看在ApiDemos 中的 LocalService.java 類和 LocalServiceActivities.java。
如下歸納了Messenger的使用方法:
Handler
,用於客戶端每次調用時接收回調Handler
用於建立一個 Messenger
對象(它是一個對 Handler
的引用)Messenger
對象建立一個 IBinder
,服務在 onBind() 中把它返回給客戶端IBinder
將 Messenger
(引用服務的 Handler
)實例化,客戶端用它向服務發送消息對象 Message
Handler
中的每一個消息 Message
——確切的說,是在 handleMessage() 方法中接收在 MessengerService.java(服務)和 MessengerServiceActivities.java(客戶端)例程中,能夠看到如何關於 Messenger 的實用例子。