處理生命週期
androidx.lifecycle
軟件包提供了可用於構建生命週期感知型組件的類和接口 - 這些組件可以根據 Activity 或 Fragment 的當前生命週期狀態自動調整其行爲。
dependencies {
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"
}
示例:
internal class MyLocationListener(
private val context: Context,
private val callback: (Location) -> Unit
) {
fun start() {
// connect to system location service
}
fun stop() {
// disconnect from system location service
}
}
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
// update UI
}
}
public override fun onStart() {
super.onStart()
myLocationListener.start()
// manage other components that need to respond
// to the activity lifecycle
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
// manage other components that need to respond
// to the activity lifecycle
}
}
雖然此示例看起來沒問題,但在真實的應用中,最終會有太多管理界面和其他組件的調用,以響應生命週期的當前狀態。管理多個組件會在生命週期方法(如
onStart()
和onStop()
)中放置大量的代碼,這使得它們難以維護。此外,無法保證組件會在 Activity 或 Fragment 停止之前啓動。在我們需要執行長時間運行的操作(如
onStart()
中的某種配置檢查)時尤其如此。這可能會導致出現一種競爭條件,在這種條件下,onStop()
方法會在onStart()
之前結束,這使得組件留存的時間比所需的時間要長。
androidx.lifecycle
軟件包提供的類和接口可幫助您以彈性和隔離的方式解決這些問題。
生命週期
Lifecycle
是一個類,用於存儲有關組件(如 Activity 或 Fragment)的生命週期狀態的信息,並允許其他對象觀察此狀態。
Lifecycle
使用兩種主要枚舉跟蹤其關聯組件的生命週期狀態:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JcoqOlsR-1592584470619)(/Users/liubo/Desktop/筆記/Jetpack/lifecycle-states.svg)]
圖 1. 構成 Android Activity 生命週期的狀態和事件
類可以通過向其方法添加註解來監控組件的生命週期狀態。然後,您可以通過調用 Lifecycle
類的 addObserver()
方法並傳遞觀察者的實例來添加觀察者,如以下示例中所示:
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(MyObserver())
在上面的示例中,
myLifecycleOwner
對象實現了LifecycleOwner
接口,我們將在接下來的部分中對該接口進行說明。
LifecycleOwner
LifecycleOwner
是單一方法接口,表示類具有 Lifecycle
。它具有一種方法(即 getLifecycle()
),該方法必須由類實現。如果您嘗試管理整個應用進程的生命週期,請參閱 ProcessLifecycleOwner
。
實現 LifecycleObserver
的組件可與實現 LifecycleOwner
的組件無縫協同工作,因爲所有者可以提供生命週期,而觀察者可以註冊以觀察生命週期。
對於位置跟蹤示例,我們可以讓
MyLocationListener
類實現LifecycleObserver
,然後在onCreate()
方法中使用 Activity 的Lifecycle
對其進行初始化。這樣,MyLocationListener
類便可以“自給自足”,這意味着,對生命週期狀態的變化做出響應的邏輯會在MyLocationListener
(而不是在 Activity)中進行聲明。讓各個組件存儲自己的邏輯,可使 Activity 和 Fragment 邏輯更易於管理。
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this, lifecycle) { location ->
// update UI
}
Util.checkUserStatus { result ->
if (result) {
myLocationListener.enable()
}
}
}
}
一個常見的用例是,如果 Lifecycle
現在未處於良好的狀態,則應避免調用某些回調。例如,如果回調在 Activity 狀態保存後運行 Fragment 事務,就會引發崩潰,因此我們絕不能調用該回調。
爲簡化此用例,Lifecycle
類允許其他對象查詢當前狀態。
internal class MyLocationListener(
private val context: Context,
private val lifecycle: Lifecycle,
private val callback: (Location) -> Unit
) {
private var enabled = false
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
if (enabled) {
// connect
}
}
fun enable() {
enabled = true
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
// disconnect if connected
}
}
對於此實現,
LocationListener
類可以完全感知生命週期。如果我們需要從另一個 Activity 或 Fragment 使用LocationListener
,只需對其進行初始化。所有設置和拆解操作都由類本身管理。
實現自定義LifecycleOwner
Support Library 26.1.0 及更高版本中的 Fragment 和 Activity 已實現 LifecycleOwner 接口。
如果您有一個自定義類並希望使其成爲 LifecycleOwner
,您可以使用 LifecycleRegistry 類,但需要將事件轉發到該類,如以下代碼示例中所示:
class MyActivity : Activity(), LifecycleOwner {
private lateinit var lifecycleRegistry: LifecycleRegistry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleRegistry = LifecycleRegistry(this)
lifecycleRegistry.markState(Lifecycle.State.CREATED)
}
public override fun onStart() {
super.onStart()
lifecycleRegistry.markState(Lifecycle.State.STARTED)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
生命週期感知型組件的最佳做法
- 使界面控制器(Activity 和 Fragment)儘可能保持精簡。它們不應試圖獲取自己的數據,而應使用
ViewModel
執行此操作,並觀察LiveData
對象以將更改體現到視圖中。 - 設法編寫數據驅動型界面,對於此類界面,界面控制器的責任是隨着數據更改而更新視圖,或者將用戶操作通知給
ViewModel
。 - 將數據邏輯放在
ViewModel
類中。ViewModel
應充當界面控制器與應用其餘部分之間的連接器。不過要注意,ViewModel
不負責獲取數據(例如,從網絡獲取)。ViewModel
應調用相應的組件來獲取數據,然後將結果提供給界面控制器。 - 使用 Data Binding 在視圖與界面控制器之間維持乾淨的接口。這樣一來,您可以使視圖更具聲明性,並儘量減少需要在 Activity 和 Fragment 中編寫的更新代碼。如果您更願意使用 Java 編程語言執行此操作,請使用諸如 Butter Knife 之類的庫,以避免樣板代碼並實現更好的抽象化。
- 如果界面很複雜,不妨考慮創建 presenter 類來處理界面的修改。這可能是一項艱鉅的任務,但這樣做可使界面組件更易於測試。
- 避免在
ViewModel
中引用View
或Activity
上下文。 如果ViewModel
存在的時間比 Activity 更長(在配置更改的情況下),Activity 將泄露並且不會由垃圾回收器妥善處置。 - 使用 Kotlin 協程管理長時間運行的任務和其他可以異步運行的操作。
生命週期感知型組件的用例
生命週期感知型組件可使您在各種情況下更輕鬆地管理生命週期。下面列舉幾個例子:
- 在粗粒度和細粒度位置更新之間切換。使用生命週期感知型組件可在位置應用可見時啓用細粒度位置更新,並在應用位於後臺時切換到粗粒度更新。藉助生命週期感知型組件
LiveData
,應用可以在用戶使用位置發生變化時自動更新界面。- 停止和開始視頻緩衝。使用生命週期感知型組件可儘快開始視頻緩衝,但會推遲播放,直到應用完全啓動。此外,應用銷燬後,您還可以使用生命週期感知型組件終止緩衝。
- 開始和停止網絡連接。藉助生命週期感知型組件,可在應用位於前臺時啓用網絡數據的實時更新(流式傳輸),並在應用進入後臺時自動暫停。
- 暫停和恢復動畫可繪製資源。藉助生命週期感知型組件,可在應用位於後臺時暫停動畫可繪製資源,並在應用位於前臺後恢復可繪製資源。