之前幾篇文章簡單梳理了在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
的設計還可以進行進程間通信。具體又是如何編寫代碼搭建進程間的溝通橋梁呢?敬請期待后續文章。