Android Lifecycle詳解(一)
官方文件翻譯
使用生命週期感知元件處理生命週期
生命週期感知元件響應於另一元件的生命週期狀態(如Activity和Fragment)的變化而執行動作。這些元件有助於產生更好的組織性和更輕的重量程式碼,這更易於維護。
一種常見的模式是在Activity和Fragment的生命週期方法中實現依賴元件的動作。然而,這種模式導致程式碼的組織和錯誤擴散。通過使用生命週期感知元件,可以將依賴元件的程式碼從生命週期方法中移入元件本身。
android.arch.lifecycle包提供了類和介面,這些類和介面允許您構建生命週期感知元件,這些元件可以根據Activity或Fragment的當前生命週期狀態自動調整其行為。
Lifecycle的依賴,包括LiveData和 ViewModel。
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - just ViewModel
implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // use -ktx for Kotlin
// alternatively - just LiveData
implementation "android.arch.lifecycle:livedata:$lifecycle_version "
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
// alternately - if using Java8, use the following instead of compiler
implementation "android.arch.lifecycle:common-java8:$lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"
// optional - Test helpers for LiveData
testImplementation "android.arch.core:core-testing:$lifecycle_version"
}
AndroidX
dependencies {
def lifecycle_version = "2.0.0-beta01"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
// alternatively - just ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" // use -ktx for Kotlin
// alternatively - just LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData). Some UI
// AndroidX libraries use this lightweight import for Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" // use kapt for Kotlin
// 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:$lifecycle_version" // use -ktx for Kotlin
// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$lifecycle_version"
}
在Android框架中定義的大多數應用程式元件都有生命週期。生命週期是由作業系統或執行在您的程序中的框架程式碼管理的。它們是Android工作的核心,你的應用程式必須依賴於它們。不這樣做可能觸發記憶體洩漏或甚至應用崩潰。
我們有一個Activity用於顯示螢幕上的裝置位置。一個常見的實現方式可能如下:
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// connect to system location service
}
void stop() {
// disconnect from system location service
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
@Override
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// update UI
});
}
@Override
public void onStart() {
super.onStart();
myLocationListener.start();
// manage other components that need to respond
// to the activity lifecycle
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
// manage other components that need to respond
// to the activity lifecycle
}
}
儘管這個示例看起來很好,但在實際應用程式中,由於響應生命週期的當前狀態,最終有太多的呼叫用於管理UI和其他元件。管理多個元件在生命週期方法中放置相當數量的程式碼,例如onStart()和onStop(),這使得它們難以維護。
此外,不能保證元件在activity 或者 fragment 停止之前啟動。如果我們需要執行一個長時間執行的操作,如在onStart()中執行的一些配置的檢查,則尤其如此。這會導致onStop()方法在onStart()方法之前完成執行的競爭條件,使元件活的時間比它需要的時間更長。
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// update UI
});
}
@Override
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
android.arch.lifecycle包提供了類和介面,幫助您以彈性和獨立的方式解決這些問題。
Lifecycle
Lifecycle是一個包含元件生命週期狀態(如activity或fragment)資訊的類,並允許其他物件觀察該狀態。
Lifecycle主要使用兩個列舉來跟蹤其相關元件的生命週期狀態:
Event
從框架和Lifecycle類中分派的生命週期事件。這些事件對映到activities和fragments中的回撥事件。
State
Lifecycle物件跟蹤的元件的當前狀態。
把狀態看作圖和事件的節點,作為這些節點之間的邊緣。
一個類可以通過向其方法添加註解來監視元件的生命週期狀態。然後,您可以通過呼叫Lifecycle類的addObserver()方法新增一個觀察者,並傳遞觀察者的例項,如下面的示例所示:
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
LifecycleOwner是一個單一的方法介面,表示該類具Lifecycle。它有一個方法,getLifecycle(),它必須被實現。如果您試圖管理整個應用程式的生命週期,請參閱ProcessLifecycleOwner。
該介面從單個類(如Fragment和AppCompatActivity)抽象出Lifecycle的所有權,並允許編寫與其共同工作的元件。任何自定義應用程式類都可以實現LifecycleOwner介面。
實現LifecycleObserver的元件與實現LifecycleOwner的元件無縫協作,因為所有者可以提供一個生命週期,觀察者可以註冊來監視該生命週期。
對於位置跟蹤示例,我們可以使MyLocationListener類實現LifecycleObserver,然後在activity的Lifecycle的onCreate()方法中初始化它。這允許MyLocationListener類自給自足,意味著對生命週期狀態的變化做出反應的邏輯在MyLocationListener中定義而不是activity。讓各個元件儲存它們自己的邏輯,使得activities和fragments邏輯更易於管理。
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// update UI
});
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}
如果Lifecycle現在不處於一個合適的狀態,常見的做法是避免呼叫某些回撥。例如,如果一個回撥函式執行fragment的事務是在活動狀態儲存之後,它將觸發崩潰,因此我們將永遠不想呼叫該回調。
為了使用例變得簡單,Lifecycle類允許其他物件查詢當前狀態。
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) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
通過這個實現,我們的LocationListener類完全是生命週期感知的。如果我們需要從另一個activity或fragment中使用我們的LocationListener,我們只需要初始化它。所有的設定和拆卸操作都是由類本身管理的。
如果一個庫提供的類需要與Android lifecycle 一起使用,我們建議您使用生命週期感知元件。您客戶端的lib可以輕鬆地將這些元件整合在客戶端上而無需手動管理生命週期。
實現一個自定義的LifecycleOwner
支援庫26.1.0中的Fragment和Activities已經實現了LifecycleOwner介面.
如果您有一個自定義類,您想建立一個LifecycleOwner,您可以使用LifecycleRegistry類,但是您需要將事件轉發到該類中,如下面的程式碼示例所示:
public class MyActivity extends Activity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
}
@Override
public void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
生命週期感知元件的最佳實踐
保持UI控制器(activities和fragments)儘可能輕量級。他們不應該試圖獲取他們自己的資料;相反,使用ViewModel來做這件事,並觀察LiveData物件,以將資料變化返回給檢視。
嘗試編寫資料驅動的UI,其中UI控制器的職責是在資料更改時更新檢視,或將使用者操作通知回ViewModel。
將你的資料邏輯放在ViewModel類中。ViewModel應充當UI控制器和應用程式其餘部分之間的聯結器。但是要注意,ViewModel的責任不是用來獲取資料(例如,從一個網路獲取資料)。相反,ViewModel應該呼叫適當的元件來獲取資料,然後將結果提供給UI控制器。
使用Data Binding來維護View和UI控制器之間的乾淨介面。這可以讓您的檢視更具宣告性,並最小化您需要在activities和fragments中寫入的更新程式碼。如果你習慣用Java程式語言做這件事,使用像Butter Knife這樣的庫來避免樣板程式碼並有更好的抽象。
如果UI複雜,請考慮建立一個presenter類來處理UI修改。這可能是一項費力的任務,但它可以使UI元件更容易測試。
避免在ViewModel中引用View或者Activity的上下文。如果ViewModel超出了activity(在配置改變的情況下),那麼您的activity就會記憶體洩漏,並且不能被垃圾收集器正確地處理。
生命週期感知元件的使用場景
生命週期感知元件可以使您在各種情況下更容易管理生命週期。舉幾個例子:
在粗粒度和細粒度位置更新之間切換。使用生命週期感知的元件,使細粒度的位置更新,而您的位置應用程式是可見的,並切換到粗粒度更新時,應用程式是在後臺。LiveData是一個生命週期感知元件,允許你的app自動更新UI,在使用者更改位置的時候。
停止和啟動視訊緩衝。使用生命週期感知的元件儘快啟動視訊緩衝,但推遲播放直到應用程式完全啟動。當應用程式被銷燬時,還可以使用生命週期感知元件來終止緩衝。
啟動和停止網路連線。當應用程式處於前臺,使用生命週期感知的元件來啟用網路資料的實時更新(流),並且當應用程式進入後臺時也會自動暫停。
停止和恢復動畫drawables。當應用程式處於後臺時,使用生命週期感知的元件來停止動畫,當應用程式處於前臺之後恢復動畫播放。
停止事件處理
當Lifecycle屬於AppCompatActivity或Fragment時,生命週期的狀態將更改為CREATED,而當AppCompatActivity或Fragment的onSaveInstanceState()被呼叫時,ON_STOP事件將被排程。
當通過onSaveInstanceState()儲存Fragment或AppCompatActivity的狀態時,其UI被認為是不可變的,直到呼叫ON_START。試圖在儲存狀態後修改UI可能會導致應用程式導航狀態的不一致,這是為什麼如果在儲存狀態後應用程式執行FragmentTransaction,則FragmentManager丟擲異常。有關詳細資訊,請參閱commit()。
LiveData阻止了它的邊緣情況,通過阻止呼叫observer,如果observer關聯的Lifecycle不是在STARTED狀態。在幕後,它呼叫isAtLeast(),然後決定呼叫它的observer。
不幸的是,AppCompatActivity的onStop()方法是在onSaveInstanceState()之後呼叫的,它留下了一個不允許UI狀態改變的間隙,但是Lifecycle還沒有被移動到建立的狀態。
為了防止這種情況,BETA2版本以及更低版本標記這個狀態為CREATED,並且沒有分發這個事件,以至於任何程式碼段檢查當前的狀態來獲取真正的值,儘管事件在 onStop()被呼叫的時候 才會被分發。
不幸的是,這個解決方案有兩個主要問題:
在API級別23和更低的情況下,Android系統實際上儲存了活動的狀態,即使它被另一個activity部分覆蓋。換句話說,Android系統呼叫onSaveInstanceState(),但它不一定呼叫onStop()。這就形成了一個潛在的長間隔,以至於觀察者仍然認為生命週期是活動的,而此時它的UI狀態不能被修改。
任何想要向LiveData類暴露類似行為的類都必須實現Lifecycle Beta2版本或者更低版本提供的解決方案。
注意: 為了使流程更簡單,並與舊版本提供更好的相容性,從版本1.0.0 RC1開始,Lifecycle物件被標記為CREATED,當onSaveInstanceState()被呼叫時, ON_STOP事件被分發,而不需等待呼叫onStop()方法。這不太可能影響您的程式碼,但這裡你需要注意,因為它與API級別26和更低的Activity類中的呼叫順序不匹配。
附加資源
嘗試lifecycle 元件 codelab.
生命週期感知元件是Android Jetpack的一部分。在Sunflower演示應用程式中可以看到他們。