[劃重點] Service 知識要點
來總結一下 Service 的幾個知識要點,包括啟動方式、每種啟動方式的生命週期、IntentService 原始碼分析,以及 boundService 三種使用方法。
Service 三種啟動方式
-
前臺 Service (startForegroundService(intent)): 在 API 大於等於 26 以上,用來設定通知欄。在呼叫這個 api 之後應馬上呼叫 Service#startForeground(int, Notification),來設定 Notification,否則系統會自動停止 Service,併發生 ANR。
-
後臺 Service (startService(intent)): 如果 app 的一些操作不需要通知使用者,則啟動後臺 Service。
注:API 大於等於 26 時,當 app 不在前臺的時候,後臺服務會受到限制,這種情況下,官方文件推薦使用 Schedule jobs。
- 繫結 Service (bindService(intent, serviceConnection, flags)): 繫結 Service 提供客戶端/服務端模式,元件之間可以用過它傳送、接受訊息,甚至可以跨程序通訊。在呼叫 unbindService(),Service 就會銷燬。
生命週期
startService(): onCreate() -> onStartCommand()/onStart() -> … -> onDestroy()
bindService(): onCreate() -> onBind() -> … -> onUnbind() -> onDestroy()
以上兩個生命週期只是對單個 activity start 或者 bind 服務的時候,然而在實際使用的時候,通常會兩個結合使用,也或者是多個 activity 對同一個 service 繫結。
- 多個 Activity StartService 時,Service 不會重複啟動,onStartCommand() 會重複執行,並獲取對應的 intent。當有一個 stopService() 時,Service 會銷燬。
- 多個 Activity 繫結和解綁時,Service 會重複走 onBind()/onUnbind()這兩個生命週期,但不會銷燬,直到沒有 Activity 和它有繫結關係的時候,才會銷燬。
- 當 StartService/bindService 混合用時,Service 的銷燬時機為 stopService。
IntentService 原理
大家都知道,IntentService 的特性是執行在非 UI 執行緒,可執行耗時操作,當執行完後會自動銷燬。我們今天在看一下它的原理:
IntentService 內部由 HandlerThread 維護一個執行緒,在啟動後獲取 intent 併發送給 HandlerThread 所在的執行緒執行,執行完任務後自動呼叫 stopSelf() 銷燬。
具體的原始碼實現如下:
- 繼承 Service;
- onBind(): IntentService 由 startService() 啟動,當然不需要 binder,所以這個函式返回 null;
-
onCreate(): 新建 HandlerThread 並啟動執行緒,通過 HandlerThread.getLooper() 新建 Handler。總結起來兩句程式碼,大致如下:
new HandlerThread().start(); new ServiceHandler(thread.getLooper());
-
onStart(): 這個函式中,通過前面新建的 Handler,把 intent 訊息傳送到非 UI 執行緒,即:mServiceHandler.sendMessage(msg)。
- ServiceHandler 的 handleMessage 函式實現中,先執行抽象函式 onHandleIntent,此函式中即是我們想要在 IntentService 中執行的任務。在函式執行結束後呼叫 stopSelf(msg.arg1) 終止服務,這也就是為什麼任務執行完畢後會自動銷燬的原因。
- onDestroy(): 在此方法中退出之前建立的執行緒,即:mServiceLooper.quit()。
boundService 三種使用方法
- 繼承 Binder:實現內部類繼承 Binder,並在 onBind() 中返回。此中方法不能實現 IPC。
- 使用 Messenger:Messenger 服務端需要傳入 Handler 做引數,因此在 Service 中實現 Handler 內部類,建立 Messenger,並在 onBind() 中返回 mMessenger.binder。客戶端通過 IBinder 作為引數新建 Messenger 例項,這樣便可與服務端通訊。Messenger 內部實現使用 Binder,支援 IPC,因此此中方式可用於跨程序通訊。
- AIDL:新建 AIDL 檔案,編譯器會自動生成對於的 java 類,之後在 Service 實現自動生成類的介面方法,onBind() 方法中返回實現類的 asBinder() 方法,客戶端中在 ServiceConnection 回撥中通過 asInterface(IBinder) 獲取到遠端類通訊。
關於 AIDL、Binder 的相關知識,為想單拿出一篇部落格來說,所以這裡就先簡單說一下使用方法。歡迎關注我的微信公眾平臺(碼小豬),如有更新會及時釋出。