Android系統編程入門系列之加載服務Service


之前幾篇文章簡單梳理了在Android系統的四大組件之一,最主要的界面Activity中,使應用程序與用戶進行交互響應的相關知識點,那對於應用程序中不需要與用戶交互的邏輯,又要用到哪些內容呢?本文開始將介紹應用程序無需界面交互的內部交互相關知識點,首先從另外一個四大組件之一的服務Service開始。

清單文件一文的組件聲明中,已經知道服務Service與界面Activity一樣,都要在清單文件中注冊聲明。同樣的,每個注冊聲明的服務Service類向上追溯都必須繼承自android.app.Service父類,因此服務Service也有自己的生命周期。

生命周期

Service主要負責應用程序中不需要界面展示或交互的長時間操作,像是播放音樂,網絡請求等都是可以在服務Service中完成的。與界面Activity的生命周期一樣,在服務Service的聲明周期內不允許執行耗時操作,所以,雖然服務Service中可以進行長時間操作,但是仍然需要將這部分耗時操作放入非Android系統主線程中。

(調用構造方法)對象實例化

首次啟動新的服務Service時,系統會申請內存空間存儲該服務Service的實例化對象。服務Service兩種啟動方式,其一與界面Activity一樣需要借助意圖Intent對象,調用上下文環境Context對象的startService(Intent service)啟動。

其二是進程間通信時所用的方式,不僅要借助意圖Intent對象,還要使用實現android.content.ServiceConnection接口的對象,進而調用上下文環境Context對象的bindService(Intent service, ServiceConnection conn, int flags)方法,而參數三 flags 標記了啟動該服務Service時所綁定的模式,通常使用Context.BIND_AUTO_CREATE標記會自動創建當前綁定的服務Service。除此之外,還可以按位或的形式追加其他標記,例如追加|Context.BIND_NOT_FOREGROUND標記當前綁定服務Service為較低優先級的后台服務,在手機息屏或高能耗時,系統會優先殺死低優先級的服務;而追加|Context.BIND_IMPORTANT標記可以提升綁定服務Service的優先級為前台服務,在手機息屏或高能耗時,只要當前應用程序存活,當前服務Service就不會被殺死;如果追加|Context.BIND_ABOVE_CLIENT標記當前綁定服務Service的優先級要高於當前應用程序,當手機息屏或高能耗時,系統可能會先殺死應用程序,但是當前服務Service仍然存活。

Android系統為不同進程間的通信提供了一套AIDL語言規范,詳情將在以后的文章中介紹,這里只需了解在實現ServiceConnection接口中,要重寫兩個方法,分別是在進程通信接口與服務Service連接成功后回調的onServiceConnected(ComponentName name, IBinder service)方法,和連接斷開之后回調的onServiceDisconnected(ComponentName name)方法。

上述兩種啟動方式,都會觸發系統在當前應用程序的清單文件中查找對應注冊過的服務Service,在找到之后,就會創建其實例化對象,如果在清單文件中沒有找到對應的服務Service,將不會有任何錯誤或異常。

與啟動未注冊的服務Service不同的是,當調用startActivity()系列方法啟動一個沒有在清單文件中注冊過的界面Activity時,系統通常會拋出android.content.ActivityNotFoundException異常。

(調用attachBaseContext(Context base))加載運行環境

由於Service也是android.content.ContentWrapper的子類,所以系統在創建服務Service的實例化對象后,也會優先對其加載上下文運行環境,將參數 base 作為當前應用程序的Context對象與該服務Service綁定。在該方法被調用之后的任意位置,就可以通過調用getBaseContext()等系列方法獲取並使用當前服務Service所在的上下文環境了。

(調用onCreate())服務創建

在創建服務Service並加載運行環境之后,系統會優先調用該方法,表示當前服務已經完成創建。可以重寫該方法執行一些服務內部使用的資源初始化操作。在執行完該方法之后,就標志着當前服務Service創建成功了,之后會一直處於運行狀態,同時根據上述啟動方式的不同,調用不同的生命周期方法。

(調用onStartCommand(Intent intent, int flags, int startId))服務啟動

如果是通過上述啟動方式一啟動的服務Service,每調用一次startService(),系統都會調用一次該方法。
其中參數 intent 接收每次啟動服務所傳入的Intent意圖。
參數 flags 標記當前服務多次啟動狀態,一般默認是 0 ;當該服務Service被首次調用該方法且成功返回Service.START_REDELIVER_INTENT=3后,莫名被系統殺死,之后再次啟動該服務時,標記參數則是 Service.START_FLAG_REDELIVERY=1 ;如果該服務Service被首次調用該方法並未返回結果,系統將會再次嘗試調用該方法,標記參數則為 Service.START_FLAG_RETRY=2
參數 startId 作為系統唯一值,以此標記當前服務Service
最終返回指定的int類型,如果返回默認的Service.START_STICKY_COMPATIBILITY=0,當系統殺死該服務Service后,再次調用startService()將不會再被系統回調該方法;另外如果返回Service.START_STICKY=1,在系統殺死該服務Service后,再次調用startService()將會被系統重新創建實例化並回調該方法。

(調用onBind(Intent intent))服務綁定

如果是通過上述啟動方式二啟動的服務Service,在首次調用bindService()后,系統會調用該方法,而之后如果多次調用bindService(),只有在當前服務Service已經執行完onUnbind()解綁的生命周期方法,並在解綁方法中返回 false 時,系統才會回調該方法。參數 intent 接收綁定服務所傳入的Intent意圖,最終返回android.os.IBinder接口的實現類對象。返回結果可以在bindService(Intent service, ServiceConnection conn, int flags)方法的參數二conn.onServiceConnected(ComponentName name, IBinder service)方法中接收,也就是其中的參數二IBinder類型的 service

服務解綁(調用onUnbind(Intent intent))

如果是通過上述啟動方式二啟動的服務Service,可以在bindService()綁定服務Service位置相對應的位置,調用unbindService()解綁服務Service。之后系統會回調該方法,同樣的借助參數Intent意圖實例來指定要解綁的指令信息。

服務重綁(調用onRebind(Intent intent))

如果是通過上述啟動方式二啟動的服務Service,如果當前服務Service已經執行完解綁生命周期,並在onUnbind()方法中返回 true 時,系統將會調用該方法以使用原有的IBinder對象重新綁定當前服務Service,因此該方法不需要返回值。其參數 intent 接收綁定服務所傳入的Intent意圖。最終返回值boolean類型,以標記當前服務再次被綁定時是否使用原有綁定過的IBinder對象。

服務銷毀(調用onDestroy())

啟動之后的服務Service會一直處於運行狀態,直到系統可能因能耗過過而殺死低優先級的進程時,當前服務Service將會被動殺死,或者當前服務Service主動調用代碼停止運行。與上述兩種啟動方式對應,服務Service也有兩種停止方式
其一對應於startService()啟動的服務,調用上下文環境Context對象的stopService(Intent service)方法停止運行,或者調用當前服務Service對象的stopSelf()方法也可以停止運行自身服務。
其二對應於bindService()綁定的服務,調用上下文環境Context對象的unbindService(ServiceConnection conn)方法解除綁定。由於綁定該服務的可以有多個ServiceCOnnection連接,所以必須每個bindService()綁定服務的位置都對應調用unbindService()方法主動解綁,以防止出現內存泄漏或資源占用等誤操作。

在服務Service所有綁定已解綁或主動停止運行后,系統最終會調用該方法,之后將銷毀內存中創建的該服務Service實例化對象。因此可以重寫該方法,對應於onCreate()中申請的初始化資源在該方法中釋放掉。


服務Service的生命周期與界面Activity有些類似,這也保證了在其中可以執行無用戶交互的操作,那么針對這種應用場景還需要怎么構建子線程操作呢?而且服務Service的設計還可以進行進程間通信。具體又是如何編寫代碼搭建進程間的溝通橋梁呢?敬請期待后續文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM