關於 Android Service 的介紹都在這了

概述

Service 是 Android 的四大組件之一,它主要的做用是後臺執行操做,Activity 屬於帶有 UI 界面跟用戶進行交互,而 Service 則沒有 UI 界面,全部的操做都是基於後臺運行完成。而且 Service 跟 Activity 同樣也是能夠由其它的應用程序調用啓動的,並且就算用戶切換了應用程序,Service 依舊保持運行。一個組件若是與 Service 進行了綁定( bind ),就能夠跟 Service 進行數據的交互,而且也能夠跟不一樣的進程之間進行交互 (IPC)。一般會使用到 Service 的狀況有進行網絡請求,音樂的操控,文件的 I/O 操做等。html

啓動

Service 一般是經過如下兩種方式進行啓動java

startService

當組件(例如 activity)經過調用 startService() 來啓動 Service 的時候。一旦啓動後,Service 就會獨立的在後臺運行,即便調用的組件已經銷燬了,Service 仍是能夠繼續在後臺運行。通常狀況下,只須要進行一次單獨的操做,不須要將操做後的結果返回給調用者的時候,會使用該方式啓動 Service。例如,進行上傳或者下載操做的時候,當操做完成後,Service 應該自行調用 stopService()stopSelf() 來結束運行。android

bindService

當組件(例如 activity)經過調用 bindService() 來啓動 Service 的時候。這種方式提供了 client - service 的接口,可讓調用組件跟 Service 進行發送請求及返回結果的操做,設置能夠進行進程間的通訊 (IPC)。只要有一個組件對該 Service 進行了綁定,那該 Service 就不會銷燬。而且多個組件能夠同時對一個 Service 進行綁定,只有在全部進行了綁定的組件都解綁的時候,Service 纔會銷燬git

儘管兩種方式是分開討論的,可是並非互斥的關係,使用 startService 啓動了 Service 後,也是能夠進行綁定的。github

注意: 雖然 Service 是在後臺運行,可是其實仍是在主線程裏進行全部的操做的。Service 在啓動時除非單獨進行了定義不然並無在單獨的線程或者進程了而都是在主線程裏。因此這表示任何能堵塞主線程的操做(例如音樂的播放或者網絡請求)都應該單獨開闢新的線程來進行操做,不然很容易出現 ANR 。api

方法

在建立一個 Service 時,必需要去繼承 Service,而且要重寫父類一些主要的方法來實現功能。如下是主要方法的介紹安全

onStartCommand()

系統會調用這個函數當某個組件(例如 activity,fragment)經過調用 startService() 啓動 Service 時。在該方法被調用後,Service 就會被啓動並獨立的在後臺運行。若是重寫了該方法,開發者須要在 Service 執行完操做後自行的調用 stopSelf()stopService(),來結束 Service。若是隻是會經過綁定的方式 (bind) 的方式來啓動 Service 則不須要重寫該方法網絡

onBind()

系統會調用這個函數當某個組件(例如 activity, fragment)經過調用 bindService()綁定的方式來啓動 Service 的時候。在實現這個函數的時候,必需要返回一個 IBinder 的繼承類,來與 Service 進行通訊。這個函數是默認必需要重寫的,可是若是不想經過綁定的方式來啓動 Service,則能夠直接返回 null多線程

onCreate()

系統會調用此方法在第一次啓動 Service 的時候,用於初始化一些一次性的變量。若是 Service 已經啓動了,則此方法就不會再別調用app

onDestroy()

系統在 Service 已經不須要準備被銷燬的時候會調用此方法。Service 中若有用到 threadlistenersreceivers 等的時候,應該將這些的清理方法寫在此方法內

以上就是實現一個 Service 中要實現的一些方法。

若是某個組件是經過調用 startService() 的方式來啓動了 Service,那這個 Service 就會一直在後臺運行直到 Service 內部調用 stopSelf() 或某個組件調用 stopService() 來結束該 Service

若是某個組件是經過調用 bindService() 的方式來啓動了 Service,那這個 Service 就會一直在後臺運行直到該組件與其解綁。Service 在沒有任何組件綁定的時候,系統會將其銷燬

下面的環節,將介紹若是經過上面講述的兩種方式來建立 Service

在 Manifest 裏聲明 Service

相似於 Activity,全部的 Service 都要在 Manifest 裏面進行聲明,以下:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>複製代碼

查看 標籤的官方文檔來獲取更多信息

經過在 <service> 標籤裏將 android:exported 設置爲 false。能夠防止其餘的程序來啓動你的 Service

經過 started 方式來啓動 Service

組件(例如 activity, fragment) 經過調用 startService() 方法,系統隨之調用 onStartCommand() 方法來實現 started 方式啓動 Service。

當 Service 以該形式啓動後,Service 的整個生命週期是徹底獨立的,即使啓動 Service 的組件已經被銷燬了,Service 仍是能夠在後臺無限的運行的。但開發者應該在 Service 中的操做執行完成後,調用 stopSelf() 或其它組件調用 stopService() 的方式來結束該 Service。

程序組件(例如 activity) 能夠經過傳遞一個 IntentstartService(),來實現組件與 Service 以前的數據傳遞。Service 是經過系統調用的 onStartCommand() 方法接受傳遞的Intent ,完成整個數據傳遞過程。

注意: Service 自己默認是運行在主線程裏的,因此若是在 Service 要進行一些會堵塞線程的操做,必定要將這些操做放在一個新的線程裏。

Android 的框架提供了 IntentService 來知足後臺運行異步線程的需求。

IntentService

IntentService 是 Service 的子類,而且全部的請求操做都是在異步線程裏。若是不須要 Service 來同時處理多個請求的話,IntentService 將會是最佳的選擇。只須要繼承並重寫 IntentService 中的 onHandleIntent() 方法,就能夠對接受到的 Intent 作後臺的異步線程操做了。

IntentService 提供了以下的幾個功能:

  • 會建立一個異步線程來處理全部從程序主線程發送到 onStartCommand() 的 intents 。
  • 建立了一個隊列池來保證每次只有一個 Intent 傳遞到 onHandleIntent() 方法中,避免了多線程問題。
  • 提供默認 onBind() 方法的實現,返回 null
  • 提供默認 onStartCommand() 方法的實現,將收到的 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() 的返回值來實現的

START_NOT_STICKY

當系統因回收資源而銷燬了 Service,當資源再次充足時不自動啓動 Service。除非還有爲處理的 Intent 準備發送。當你的程序能夠很容易的從新開啓未完成的操做時,這是最安全的避免 Service 在沒必要要的狀況下啓動的選項。

START_STICKY

當系統因回收資源而銷燬了 Service,當資源再次充足時自動啓動 Service,而且再次調用 onStartCommand() 方法,可是不會傳遞最後一次的 Intent,相反系統在回調onStartCommand() 的時候會傳一個空 Intent 。除非還有爲處理的 Intent 準備發送

START_REDELIVER_INTENT

當系統因回收資源而銷燬了 Service,當資源再次充足時自動啓動 Service,而且再次調用 onStartCommand() 方法,並會把最後一次 Intent 再次傳遞給,onStartCommand(),相應的在隊列裏的 Intent 也會按次序一次傳遞。此模式適用於下載等服務。

Start Service

該方式容許多個組件同時對相同的 Service 進行 startService 操做,可是若是隻要有其中有一個組件調用了 stopSelf()stopService(), 該 Service 就會被銷燬

Intent intent = new Intent(this, HelloService.class);
startService(intent);複製代碼
Stop Service

當有多個組件進行了 startService 操做時,不該該直接的去調用 stopSelf()stopService() 來結束 Service, 由於這會對其餘已經發起請求的操做產生影響,故在 onStartCommand() 方法中會接受一個 startId, 而後在結束 Service 時,調用 stopService(int) 方法來只是結束一個特定的請求,從而達到保護其餘請求不受影響的目的

經過 Bind 方式啓動 Service

當應用程序中的 activity 或其它組件須要與服務進行交互,或者應用程序的某些功能須要暴露給其它應用程序時,你應該建立一個 Bind 服務,並經過進程間通訊(IPC)來完成。

Service 只在爲綁定的應用程序組件工做時纔會存活,所以,只要沒有組件綁定到服務,系統就會自動銷燬服務

獲取 IBinder 實例
擴展 Binder 類

若是服務是你的應用程序所私有的,而且與客戶端運行於同一個進程中(一般都是如此),你應該經過擴展 Binder 類來建立你的接口,並從 onBind() 返回一個它的實例。客戶端接收該 Binder 對象並用它來直接訪問 Binder 甚至 Service中可用的公共方法。

若是你的服務只是爲你本身的應用程序執行一些後臺工做,那這就是首選的技術方案。不用這種方式來建立接口的理由只有一個,就是服務要被其它應用程序使用或者要跨多個進程使用。

使用 Messenger

若是你須要接口跨越多個進程進行工做,能夠經過 Messenger 來爲服務建立接口。在這種方式下,服務定義一個響應各種消息對象 MessageHandler。此 HandlerMessenger 與客戶端共享同一個 IBinder 的基礎,它使得客戶端能夠用消息對象 Message向服務發送指令。此外,客戶端還能夠定義本身的 Message ,以便服務可以往回發送消息。

這是執行進程間通訊(IPC)最爲簡便的方式,由於 Messenger 會把全部的請求放入一個獨立進程中的隊列,這樣你就不必定非要把服務設計爲線程安全的模式了。

使用 AIDL

絕大多數應用程序都不該該用 AIDL 來建立 bind 服務,由於這可能須要多線程處理能力而且會讓代碼變得更爲複雜。 所以,AIDL 對絕大多數應用程序都不適用,而且本文也不會討論如何在服務中使用它的內容。若是你確信須要直接使用 AIDL,那請參閱 AIDL 文檔。

擴展 Binder 類

若是你的服務只用於本地應用程序而且不須要跨進程工做,那你只要實現本身的 Binder 類便可,這樣你的客戶端就能直接訪問服務中的公共方法了。

注意:僅當客戶端和服務位於同一個應用程序和進程中,這也是最多見的狀況,這種方式纔會有用。好比,一個音樂應用須要把一個 activity 綁定到它本身的後臺音樂播放服務上,採用這種方式就會很不錯。

如下是設置步驟:

  1. 在你的服務中,建立一個 Binder 的實例,其中實現如下三者之一:
    • 包含了可供客戶端調用的公共方法
    • 返回當前 Service 實例,其中包含了可供客戶端調用的公共方法
    • 或者,返回內含 Service 類的其它類的一個實例,Service 中包含了可供客戶端調用的公共方法
  2. 從回調方法 onBind() 中返回 Binder 的該實例
  3. 在客戶端中,在回調方法 onServiceConnected() 中接收 Binder 並用所提供的方法對綁定的服務進行調用

    注意:
    服務和客戶端之因此必須位於同一個應用程序中,是爲了讓客戶端可以正確轉換(cast)返回的對象並調用對象的 API。 服務和客戶端也必須位於同一個進程中,由於這種方式不能執行任何跨進程的序列化(marshalling)操做。

具體的實用案例請查看在ApiDemos 中的 LocalService.java 類和 LocalServiceActivities.java

使用 Messenger

如下歸納了Messenger的使用方法:

  • 服務實現一個 Handler ,用於客戶端每次調用時接收回調
  • Handler 用於建立一個 Messenger 對象(它是一個對 Handler 的引用)
  • Messenger 對象建立一個 IBinder ,服務在 onBind() 中把它返回給客戶端
  • 客戶端用 IBinderMessenger(引用服務的 Handler)實例化,客戶端用它向服務發送消息對象 Message
  • 服務接收 Handler 中的每一個消息 Message ——確切的說,是在 handleMessage() 方法中接收

MessengerService.java(服務)和 MessengerServiceActivities.java(客戶端)例程中,能夠看到如何關於 Messenger 的實用例子。

Service 的生命週期

service 生命週期圖
相關文章
相關標籤/搜索