生命周期感知組件可以感知其他組件的生命周期,例如 Activity,Fragment等,以便於在組件的生命周期狀態變化時做出相應的操作。支持生命感知的組件可以幫你更好的組織代碼,讓你的代碼更輕,更好維護。 ...
奉上翻譯原文地址: 處理生命周期 ;翻譯過程中加上了自己的一點理解。理解不對的地方直接評論就好。
生命周期感知組件可以感知其他組件的生命周期,例如 Activity,Fragment等,以便於在組件的生命周期狀態變化時做出相應的操作。支持生命感知的組件可以幫你更好的組織代碼,讓你的代碼更輕,更好維護。
對於需要響應生命周期變化的組件,我們通常是在 Activity
和 Fragment
的生命周期方法里實現一些操作。然而,這種模式會導致代碼不好管理,容易出現錯誤。通過支持生命周期的組件,可以將原本在生命周期方法里的操作移到組件內部。
androidx.lifecycle
包提供的介面和類可以幫助我們構建可感知生命周期的組件,這些組件就可以根據 Activity 或者 Fragment 的生命周期狀態自行調整行為。
在項目添加生命周期感知組件的依賴,可以參加這個頁面:傳送門
//包含 ViewModel 和 LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
// 或者 - 只包含 ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// Kotlin使用 lifecycle-viewmodel-ktx
// implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// 或者,只包含 LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// 或者,只有 Lifecycle(沒有 LiveData,ViewModel)
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
// Kotlin 使用 kapt 替代 annotationProcessor
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// 如果使用了 Java8 使用這個替代上面的 lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// 可選 - ReactStreams 對 LiveData 的支持
implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"
//Kotlin 使用 lifecycle-reactivestreams-ktx
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
// 可選 LiveData 的測試
testImplementation "androidx.arch.core:core-testing:$lifecycle_version"
如果使用的是 Kotlin 記得添加 kotlin-kapt
插件
Android 框架中定義的大多數應用組件都具有生命周期。生命周期是由操作系統或框架代碼管理的。
雖然組件的生命周期不由我們控制,但是我們必須尊重組件的生命周期,不然很可能會導致記憶體泄漏甚至崩潰。
假如我們有個 Activity 在屏幕上顯示設備位置信息,最常見的實現可能就是這樣了:
Kotlin
internal class MyLocationListener(
private val context: Context,
private val callback: (Location) -> Unit
) {
fun start() {
// 連接系統位置服務
}
fun stop() {
// 斷開系統位置服務
}
}
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
// 更新 UI
}
}
public override fun onStart() {
super.onStart()
myLocationListener.start()
// 管理其他需要響應 Activity 生命周期的組件
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
// 管理其他需要響應 Activity 生命周期的組件
}
}
Java
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// 連接系統位置服務
}
void stop() {
// 斷開系統位置服務
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
@Override
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// 更新 UI
});
}
@Override
public void onStart() {
super.onStart();
myLocationListener.start();
// 管理其他需要響應 Activity 生命周期的組件
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
// 管理其他需要響應 Activity 生命周期的組件
}
}
目前看起來這樣還不錯,但在真實情況下,可能還會有其他需要響應生命周期的組件,也有可能是在 onStart()
和 onStop()
。一個兩個還好,如果多了的話把這些都放在生命周期方法里,就比較難以維護。
此外,這並不能保證在 Activity 或者 Fragment 停止之前啟動我們的組件。特別是那些需要長期運行的操作,例如在 onStart()
里的檢查配置操作。這就可能會出現在 onStart()
里的操作還未啟動,而 onStop()
里卻要停止的情況。
Kotlin
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
//更新 UI
}
}
public override fun onStart() {
super.onStart()
Util.checkUserStatus { result ->
// 如果在活動停止後調用此回調該怎麼辦?
if (result) {
myLocationListener.start()
}
}
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
}
}
Java
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// 更新 UI
});
}
@Override
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// 如果在活動停止後調用此回調該怎麼辦?
if (result) {
myLocationListener.start();
}
});
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
androidx.lifecycle
包提供了一些類和介面,可幫助你以彈性和隔離的方式解決這些問題。
生命周期
Lifecycle
是一個類,它持有相關組件(例如 Activity 和 Fragment)的生命周期狀態信息並且可以讓其他對象觀察到這個狀態。
Lifecycle
使用兩個主要枚舉來跟蹤相關組件的生命周期狀態。
Event
Android
框架和 lifecycle
類發出的生命周期事件。它對應到 Activity 和 fragment 里的生命周期回調。
State
Lifecycle
類跟蹤的相關組件的當前生命周期狀態。
類可以通過添加註解來偵聽組件的生命周期事件。通過調用 Lifecycle
的 addObserver()
方法傳遞進去一個你的觀察對象即可,如下所示:
Kotlin
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(MyObserver())
Java
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
這個示例中,myLifecycleOwner
實現了 LifecycleOwner
介面。
生命周期所有者
LifecycleOwner
是一個單方法的介面,它表示這個類有生命周期。它有一個類必須實現的方法: getLifecycle()
。如果你想管理整個應用進程的生命周期可以看看這個 ProcessLifecycleOwner
這個介面從單個類中抽象出生命周期的所有權,例如 Activity 和 Fragment,可以與你寫的組件共用生命周期。任何類都可以實現 LifecycleOwner
介面。
實現 LifecycleObserver
的組件與實現 LifecycleOwner
的組件可以無縫地銜接,因為所有者可以提供生命周期,觀察者可以註冊該生命周期以觀察。
對於上面顯示位置的例子,就可以讓 MyLocationListener
實現 LifecycleObserver
,併在 Activity
的生命周期方法 onCreate()
里初始化。這樣的話 MyLocationListener
類就可以自給自足,在自己本身內部實現響應生命周期變化的邏輯處理。每個組件都在自己內部響應生命周期變化就讓 Activity
和 Fragment
的邏輯變得很清晰。
Kotlin
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this, lifecycle) { location ->
// 更新 UI
}
Util.checkUserStatus { result ->
if (result) {
myLocationListener.enable()
}
}
}
}
Java
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// 更新 UI
});
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}
一個常見的用例是,如果生命周期當前狀態不佳,則避免調用某些回調。 例如,如果回調在保存活動狀態後運行 Fragment
事務,那麼它將觸發崩潰,因此我們永遠都不想調用該回調。
為了簡化此用例,Lifecycle
類允許其他對象查詢當前狀態。 通過方法: Lifecycle.State.isAtLeast()
Kotlin
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) {
// 連接
}
}
fun enable() {
enabled = true
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
// 如果還沒連接,就去連接
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
// 如果連接了就斷開
}
}
Java
class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// 連接
}
}
public void enable() {
enabled = true;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// 如果還沒連接,就去連接
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// 如果連接了就斷開
}
}
通過以上實現,我們的 LocationListener
已經具備了感知生命周期的能力並且可以做出相應的操作。如果其他的 Activity
或者 Fragment
想使用它,只需要初始化它即可。其他所有操作都由 LocationListener
自己處理。
如果你的庫提供了需要與 Android 生命周期一起使用的類,則建議使用可識別生命周期的組件。 你的庫可以輕鬆集成這些組件,而無需在客戶端進行手動生命周期管理。
自定義生命周期所有者
支持庫 26.1.0 以及更高版本中的 Fragment
和 Activity
已經實現了 LifecycleOwner
介面。
如果想要創建 LifecycleOwner
的自定義類,則可以使用 LifecycleOwner
類,但是需要將事件轉發到該類中,如以下代碼示例所示:
Kotlin
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
}
}
Java
public class MyActivity extends Activity implements LifecycleOwner {
private LifecycleRegistry lifecycleRegistry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleRegistry = new LifecycleRegistry(this);
lifecycleRegistry.markState(Lifecycle.State.CREATED);
}
@Override
public void onStart() {
super.onStart();
lifecycleRegistry.markState(Lifecycle.State.STARTED);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
}
## 生命周期感知組件的最佳實踐
- 儘可能的讓 UI 控制器(Activity 和 Fragment) 保持精簡。讓 ViewModel 去獲取數據,數據更改通過 LiveData 響應到視圖。
- 嘗試編寫數據驅動的 UI ,其中 UI 控制器的職責是在數據更改時更新視圖,或者將用戶操作通知給 ViewModel 。
- 將數據業務邏輯放在 ViewModel 類。ViewModel 類的定位應該是 UI 控制器和應用中其他部分的連接器。但並不是說讓 ViewModel 類去獲取數據,相反的應該讓其他合適的組件去獲取數據,ViewModel 類只是把結果提供給 UI 控制器。
- 使用數據綁定庫維護視圖和 UI 控制器的整潔。這讓視圖更具聲明性,並減少在 UI 控制器的更新代碼。如果你傾向於使用 Java ,可以使用 Butter Knife 減少重覆代碼。
- 如果 UI 過於複雜,可以考試創建一個 Presenter 類管理 UI 更新,這可能更麻煩,但是可以更好的管理 UI 。
- 避免在 ViewModel 引用 View和 Activity 上下文。如果 ViewModel 生命超過 Activity (配置發生更改的情況下)可能會造成 Activity 泄漏,並且不被垃圾處理器回收。
- 使用 Kotlin 協程來管理長時間運行的任務以及可以非同步運行的其他操作。
## 生命周期感知組件的用例
生命周期感知組件可以讓你在各種情況下都很好的管理生命周期,例如:
- 在粗略和細粒度的位置更新之間切換。 使用生命周期感知組件在應用可見時啟用細粒度的位置更新,在應用處於後臺時切換到粗粒度的更新。
- 停止和開啟視頻緩衝。 使用支持生命周期的組件儘快開始視頻緩衝,但是將播放推遲到應用程式完全啟動。 還可以使用可識別生命周期的組件在應用程式銷毀時終止緩衝。
- 啟動和停止網路連接。 使用可感知生命周期的組件可以在應用程式處於前臺狀態時實時更新(流式傳輸)網路數據,併在應用程式進入後臺時自動暫停。
- 暫停和恢復動畫繪製。 當應用程式在後臺運行時,使用生命周期感知組件處理暫停動畫繪製,併在應用程式在前臺運行後恢復繪製。
處理停止事件
當生命周期屬於 AppCompatActivity
或 Fragment
時,生命周期的狀態更改為 CREATED
,並且在調用 AppCompatActivity
或 Fragment
的 onSaveInstanceState()
時調度 ON_STOP
事件。
當通過 onSaveInstanceState()
保存 Fragment
或 AppCompatActivity
的狀態時,在調用 ON_START
之前,它的 UI 被認為是不可變的。 保存狀態後嘗試修改 UI 可能會導致應用程式的導航狀態不一致,這就是為什麼如果狀態保存後應用程式運行 FragmentTransaction
,則 FragmentManager
會引發異常的原因。 詳情參見: commit()
如果觀察者的關聯生命周期至少不是 STARTED
, LiveData
不會調用觀察者,從而避免了這種極端情況。 在幕後,它在決定調用其觀察者之前調用 isAtLeast()
判斷當前狀態。
不幸的是,在 onSaveInstanceState()
之後調用了 AppCompatActivity
的 onStop()
方法,這留下了一個空白,在該空白中,不允許 UI 狀態更改,但生命周期尚未移至 CREATED
狀態。
為避免此問題,版本 beta2
及更低版本中的 Lifecycle
類將狀態標記為 CREATED
而不調度事件,因此,即使直到系統調用了 onStop()
才調度事件,任何檢查當前狀態的代碼都將獲得真實值。
不幸的是,此解決方案有兩個主要問題:
- 在 API 級別 23 和更低級別上,Android 系統實際上會保存
Activity
的狀態,即使該Activity
已被另一個Activity
部分覆蓋 。 換句話說,Android 系統調用onSaveInstanceState()
,但不一定調用onStop()
。 這將創建一個可能較長的時間間隔,在該時間間隔中,即使無法修改其 UI 狀態,觀察者仍認為生命周期處於活動狀態。 - 任何要向
LiveData
類公開類似行為的類都必須實現Lifecycle
beta 2 及更低版本提供的解決方法。
註意: 為了簡化流程並提供與舊版本的更好相容性,從版本 1.0.0-rc1 開始,生命周期對象被標記為
CREATED
,並且在調用onSaveInstanceState()
時分派ON_STOP
,而無需等待對onStop()
的調用。 這不太可能影響你的代碼,但是需要註意這一點,因為它與 API 級別 26 及更低級別的Activity
類中的調用順序不匹配。
參考資料