引入概念
-
Lifecycle解決的問題:
- 用於響應、管理其他應用組件(如
Activity
和Fragment
)的改變狀態,相對於我們自己寫事件監聽回調接口,Lifecycle
會更加簡潔、易於管理。 - 大部分應用組件都存在於
Android Framework
,生命週期綁定在此之上,並且直接由系統或者由應用進程框架管理,因此必須遵循它們的規則,避免內存泄露和應用崩潰。
- 用於響應、管理其他應用組件(如
實際場景: 我們需要在
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
}
}
- 貌似看起來很不錯,但是在實際應用中,最終會存在太多用於管理其他組件生命週期狀態的調用,管理多個組件時會在生命週期方法中放置大量代碼,例如
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: 這個事件由系統框架和
Lifecyle
類分發,並且會映射到Activit
和Fragment
的回調事件上。 -
State: 代表當前
LIfecycle
對象正在處理的組件的狀態。
-
Event: 這個事件由系統框架和
-
通過給方法添加註解的方式可以使這個類具備監聽組件生命週期的能力,然後通過
Lifecycle#addObserver()
添加觀察者即可賦予其他對象這個觀察能力,如下示例:// 作爲觀察者,我們需要實現 LifecycleObserver 接口 public class MyObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) // 在onResume時執行 public void connectListener() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) // 在onPause時執行 public void disconnectListener() { ... } } // 添加一個觀察者,使得這個觀察者也可以監聽組件狀態變化 myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifeOwner
的概念
LifeOwner
只包含一個getLifecycle()
方法,用於獲取Lifecycle
,使用時必須實現這個方法。如果想要管理整個應用進程的生命週期,可以使用ProcessLifecycleOwner
代替。這個接口從
Activity
和Fragment
等中抽取了Lifecycle
的所有權,並且允許編寫組件來與之配合,任何自定義的應用類都可以實現LifecOwner
接口。實現了
LifecycleOwner
的組件與實現了LifecycleObserver
的組件運作方式是無縫銜接的的,因爲Owner
用於提供事件,而Observer
用於註冊、監聽事件。
-
在前面我們定義了一個實現了
LifecycleObserver
接口的MyLocationLIstener
類,我們可以如下面代碼這樣在onCreate
中初始化,這意味響應生命週期變化的邏輯都提取到了MyLocationLIstener
,而不是全部擠在Activity
中,可見這樣可以極大簡化Activity
和Fragment
的代碼邏輯。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
的不合適狀態下執行回調,比如如果這個回調用於在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) { // connect } } public void enable() { enabled = true; // 查看Lifecycle的當前狀態 if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
- 通過上面實現,我們的
MyLocationListener
就完全可以管理生命週期了,如果想要在其他Activity
或Fragment
中使用它,那麼只需要初始化一下就行了,其他處理操作都會在它內部處理。 - 如果一個類庫提供需要結合
Android
生命週期的處理類,那麼建議使用Lifecycle-aware
組件,這樣的話類庫客戶端就可以輕易地整合這些組件而不需要手動地在客戶端處理生命週期管理工作。
- 爲了避免在
-
實現自定義的 LifecycleOwner
- 在
Support Library 26.1.0
以及上版本中,Fragment
和Activity
已經實現了LifecycleOwner
接口。 - 如果需要自定義實現一個
LifecycleOwner
,那麼可以使用LifecycleRegistry
類,但是你需要發送事件到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; } }
- 在
lifecycle-aware 組件最佳實踐
儘可能保證
UI
控制器,如Activity
和Fragment
的簡潔性,它們不應該請求它們自身的數據,而應交給ViewModel
去做,並觀察一個LiveData
對象,用來將變化返回給UI
視圖。儘量編寫數據驅動型(
data-driven
)UI
,這種形式下,UI
控制器只需要負責在數據改變時更新視圖,或者通知用戶動作給ViewModel
將數據邏輯放到
ViewModel
類,ViewModel
應當用作UI
控制器和應用其他部分的連接器,但是注意,ViewModel
不負責請求數據(比如網絡請求等),相反,它只是調用數據請求模塊去請求數據,然後將數據結果返回給UI
控制器。使用
DataBinding
來維持視圖與UI
控制器間的簡潔性。它可以可簡化視圖的聲明和視圖更新時所需在UI
控制器中編寫的代碼,如果喜歡使用Java
,那麼建議使用類似於ButterKnife
之類的類庫來避免編寫無聊的聲明代碼,並且它可以實現更好的抽象。如果
UI
很複雜,可以考慮創建一個Presenter
類來處理UI
更改操作,這可能很費事,但可以使UI
組件更易於測試。禁止在
ViewModel
中引用View
或者Activity
上下文(context
),否則如果ViewModel
生命週期比Activity
長時(比如configuration change
的情況),Activity
就會內存泄露而不被GC
了。
lifecycle-aware 組件使用場景
lifecycle-aware
組件可以在各種場景中讓生命週期的管理更簡單,比如以下場景:
-
粗略定位(
coarse-grained
)與高精度定位(fine-grained
)之間的更新狀態切換。使用lifecycle-aware
組件在應用處於前臺時開啓高精度定位,而在後臺時開啓粗略定位,可以結合LiveData
來實現狀態改變時更新UI
的操作。 -
開啓和關閉視頻緩衝。 比如使用
lifecycle-aware
組件儘快開啓視頻緩衝,而延遲到應用完全啓動後才真正播放視頻,同樣也可以在應用關閉時終止緩衝動作。 -
開啓和關閉網絡連接。 使用
lifecycle-aware
組件進行動態更新網絡數據,如應用處於前臺時自動加載數據,而應用切換至後臺時自動暫停加載。 -
啓動和暫停
Drawable
動畫。 前臺時播放動畫,後臺是暫停動畫。
處理 onStop 事件
當Lifecycle
關聯到AppCompatActivity
或Fragment
時,它的狀態會切換到CREATED
,而ON_STOP
狀態則是會在AppCompatActivity
或Fragment
的onSaveInstanceState()
被調用是觸發。
如果AppCompatActivity
或Fragment
是通過onSaveInstanceState()
中保存狀態的,那麼在ON_START
被調用之前,它們的UI
狀態都會被認定爲不可變的(immutable
)。這時如果嘗試在UI
狀態保存後修改UI
的話,就會導致應用導航狀態不一致,這也就是爲什麼在狀態保存後執行FragmentTransaction
,FragmentManager
會拋異常的原因了,具體看 commit()方法。
如果LiveData
的已經關聯到Lifecycle
的Observer
還沒到到達STARTED
狀態的話, LiveData
可以通過終止observer
的調用來避免上述邊角情況的發生,這是因爲LiveData
會在執行Observer
之前先調用isAtLeast()
確定狀態,然後再決定是否執行。
然而不幸的是,AppCompatActivity
的onStop()
方法實在onSaveInstanceState()
之後調用的,這種情況就導致已經保存的UI
狀態不允許改變,而Lifecycle
又還沒有到達STARTED
狀態。
爲了避免這個問題的發生,在版本beta2
及之前的Lifecycle
類都會將這一狀態標記爲CREATED
,而不分發這一事件,這樣,任何檢查當前狀態的代碼都能拿到真實狀態值,即使這一事件還沒有被分發,直到系統調用onStop()
方法。
然而又不幸的是,這個解決方案有兩大問題:
- 在
API 23
及之前的版本,Android
系統確實會保存Activity
的狀態,即使是由其他AActivity
轉換的部分,也就是說,系統調用onSaveInstanceState()
,但是確實沒有調用onStop()
必要。這造成了一個潛在的長間隔期,而在這個間隔期之間,observer
一直會認爲Lifecycle
是活動的,即使UI
狀態已經不能被改變了。 - 任何想要暴露給
LiveData
類似行爲的類都必須實現Lifecycle
在beta2
及之前版本所提供的解決方案。
Note: 爲了簡化流程併兼容老版本,請直接從版本1.0.0-rc1
開始使用,Lifecycle
對象會被標記爲CREATED
,並且會在onSaveInstanceState()
被調用是標記爲ON_STOP
狀態,而無需等待onStop()
的調用。雖然這並不會影響我們的代碼,但是確是我們需要注意的,因爲它沒有遵循API 26
及以前版本中Activity
的生命週期調用次序