【目錄】
1. Architecture Components 之 Guide to App Architecture
2. Architecture Components 之 Adding Components to your Project
3. Architecture Components 之 Handling Lifecycles
4. Architecture Components 之 LiveData
5. Architecture Components 之 ViewModel
6. Architecture Components 之 Room Persistence Library
示例程式碼連結
處理生命週期
android.arch.lifecycle 包提供了可以用於構建生命週期感知(lifecycle-aware)元件的類和介面,這些元件可以根據 Activity 或 Fragment 的當前生命週期自動調整其行為。
注:匯入 android.arch.lifecycle 到專案中請參看新增元件到專案
Android Framework 中定義的大多數應用程式元件都附有生命週期。這些生命週期由作業系統或在程式中執行的框架程式碼管理。它們是 Android 執行的核心並且應用程式必須遵守它們,不這樣做可能會導致記憶體洩漏甚至應用程式崩潰。
假設我們有一個需要在螢幕上顯示裝置位置的 Activity。常見的實現方式可能如下所示:
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// 連結到系統定位服務
}
void stop() {
// 斷開系統定位服務
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// 更新 UI
});
}
public void onStart() {
super.onStart();
myLocationListener.start();
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}複製程式碼
儘管這個例子看起來不錯,在真正的應用程式中,你最終會有太多的類似呼叫並且會導致 onStart() 和 onStop() 方法變的非常臃腫。
另外,一些元件不能在 onStart() 方法中立即啟動。如果我們需要在啟動位置觀察者之前檢查一些配置怎麼辦?在某些情況下很可能發生 Activity 停止後才檢查配置完成,這意味著在 myLocationListener.stop() 被呼叫之後 myLocationListener.start() 才被呼叫,會導致定位服務基本上永遠保持連線。
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// 更新 UI
});
}
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// 如果這個回撥在 Activity 停止後被呼叫怎麼辦?
if (result) {
myLocationListener.start();
}
});
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}複製程式碼
android.arch.lifecycle 包提供了類和介面幫助以彈性和隔離的方式解決這些問題。
Lifecycle
Lifecycle 是一個類,它持有關於元件(如 Activity 或 Fragment)生命週期狀態的資訊,並且允許其他物件觀察此狀態。
Lifecycle 使用兩個主要的列舉來跟蹤其關聯元件的生命週期狀態。
Event
生命週期事件是從框架和 Lifecycle 發出的事件。這些事件對映到 Activity 和 Fragment 中的回撥事件。
State
Lifecycle 物件跟蹤的元件的當前狀態。
將狀態視為圖表的節點,事件作為這些節點之間的邊緣。
類可以通過向其方法新增註釋來監控元件的生命週期狀態。
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());複製程式碼
LifecycleOwner
LifecycleOwner 是一個單一方法的介面,它表示實現類具有一個 Lifecycle。它有一個 getLifecycle()) 方法,該方法必須由實現類實現。
該實現類從個別的類(例如 Activity 和 Fragment)提取生命週期的所有權,並且允許編寫可與兩者相容的元件。任何自定義應用程式類都可以實現 LifecycleOwner 介面。
注:由於 Architecture Components 處於 alpha 階段,所以 Fragment 和 AppCompatActivity 不能實現 LifecycleOwner (因為我們不能在穩定的元件中新增依賴不穩定的API)。在 Lifecycle 穩定之前,為了方便提供了 LifecycleActivity 和 LifecycleFragment 類。在 Lifecycles 專案釋出後,支援庫中的 Fragment 和 Activity 將會實現 LifecycleOwner 介面;屆時 LifecycleActivity 和 LifecycleFragment 將會被棄用。另請參閱在自定義 Activity 和 Fragment 中實現 LifecycleOwner。
對於上面的例子,我們可以使 MyLocationListener 類成為 LifecycleObserver 然後在 onCreate 中使用 Lifecycle 初始化它。這樣讓 MyLocationListener 類自給自足,意味著在必要的時候它能對自己進行清理。
class MyActivity extends LifecycleActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// 更新 UI
});
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}複製程式碼
一個常見的用例是避免在 Lifecycle 處於不良狀態時呼叫某些回撥。例如,如果在儲存 Activity 狀態後回撥執行 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) {
// 連線
}
}
public void enable() {
enabled = true;
if (lifecycle.getState().isAtLeast(STARTED)) {
// 如果沒有連線則進行連線
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// 如果已經連線則斷開連線
}
}複製程式碼
通過這種實現,LocationListener 類是完全的生命週期感知(lifecycle-aware);它可以進行自己的初始化或清理操作,而不受其 Activity 的管理。如果需要在其它的 Activity 或其他的 Fragment 中使用 LocationListener,只需要初始化它。所有的安裝和解除安裝操作都由類自己管理。
可以與 Lifecycle 一起使用的類稱為生命週期感知(lifecycle-aware) 元件。鼓勵有需要使用 Android 生命週期的類的庫提供生命週期感知(lifecycle-aware) 元件,以便於使用者可以輕鬆的在客戶端整合這些類,而不需要手動管理生命週期。
LiveData 是一個生命週期感知(lifecycle-aware) 元件的示例。與 ViewModel 一起使用 LiveData 可以在遵守 Android 生命週期的前提下,更容易地使用資料填充UI。
Lifecycles 的最佳實踐
保持 UI 控制器(Activity 和 Fragment)儘可能的精簡。它們不應該試圖去獲取它們所需的資料;相反,要用 ViewModel 來獲取,並且觀察 LiveData 將資料變化反映到檢視中。
嘗試編寫資料驅動(data-driven)的 UI,即 UI 控制器的責任是在資料改變時更新檢視或者將使用者的操作通知給 ViewModel。
將資料邏輯放到 ViewModel 類中。ViewModel 應該作為 UI 控制器和應用程式其它部分的連線服務。注意:不是由 ViewModel 負責獲取資料(例如:從網路獲取)。相反,ViewModel 呼叫相應的元件獲取資料,然後將資料獲取結果提供給 UI 控制器。
使用 Data Binding 來保持檢視和 UI 控制器之間的介面乾淨。這樣可以讓檢視更具宣告性,並且儘可能減少在 Activity 和 Fragment 中編寫更新程式碼。如果你喜歡在 Java 中執行該操作,請使用像 Butter Knife 這樣的庫來避免使用樣板程式碼並進行更好的抽象化。
如果 UI 很複雜,可以考慮建立一個 Presenter 類來處理 UI 的修改。雖然通常這樣做不是必要的,但可能會讓 UI 更容易測試。
不要在 ViewModel 中引用 View 或者 Activity 的 context。因為如果 ViewModel 存活的比 Activity 時間長(在配置更改的情況下),Activity 將會被洩漏並且無法被正確的回收。
附錄
在自定義 Activity 和 Fragment 中實現 LifecycleOwner
任何自定義的 Fragment 或 Activity 都可以通過實現內建的 LifecycleRegistryOwner 介面轉換為 LifecycleOwner(而不是繼承 LifecycleActivity 或 LifecycleFragment)
public class MyFragment extends Fragment implements LifecycleRegistryOwner {
LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}複製程式碼
如果要建立一個 LifecycleOwner 的自定義類,可以使用 LifecycleRegistry 類,但是需要將事件轉發到該自定義類中。如果是 Fragment 和 Activity 實現了 LifecycleRegistryOwner 介面,則此轉發會自動完成。