引入概念
-
Lifecycle解決的問題:
- 用于響應(yīng)送讲、管理其他應(yīng)用組件(如
Activity
和Fragment
)的改變狀態(tài)蚕泽,相對于我們自己寫事件監(jiān)聽回調(diào)接口轿秧,Lifecycle
會更加簡潔、易于管理宦焦。 - 大部分應(yīng)用組件都存在于
Android Framework
发钝,生命周期綁定在此之上顿涣,并且直接由系統(tǒng)或者由應(yīng)用進程框架管理,因此必須遵循它們的規(guī)則笼平,避免內(nèi)存泄露和應(yīng)用崩潰园骆。
- 用于響應(yīng)送讲、管理其他應(yīng)用組件(如
實際場景: 我們需要在
Activity
中顯示設(shè)備的位置舔痪,通常會這樣實現(xiàn):
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
}
}
- 貌似看起來很不錯寓调,但是在實際應(yīng)用中,最終會存在太多用于管理其他組件生命周期狀態(tài)的調(diào)用锄码,管理多個組件時會在生命周期方法中放置大量代碼夺英,例如
onStart()
和onStop()
,這使得它們難以維護滋捶。 - 此外痛悯,無法保證組件在
Activity
或Fragment
停止之前啟動,這在我們需要執(zhí)行耗時長的操作時尤為真實,比如我們在onStart()
中檢查某些配置重窟,這就可能在當(dāng)onStop()
在onStart()
之間完成的情況下 產(chǎn)生競爭條件载萌,最終導(dǎo)致組件存活的時間比實際需要長。如下示例:
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
)狀態(tài)的類乖坠,并且允許其他對象觀察這一狀態(tài)。-
lifecycle
使用兩個主要枚舉類來處理與之綁定的組件的生命周期狀態(tài)刀闷。-
Event: 這個事件由系統(tǒng)框架和
Lifecyle
類分發(fā)熊泵,并且會映射到Activit
和Fragment
的回調(diào)事件上。 -
State: 代表當(dāng)前
LIfecycle
對象正在處理的組件的狀態(tài)甸昏。
-
Event: 這個事件由系統(tǒng)框架和
-
通過給方法添加注解的方式可以使這個類具備監(jiān)聽組件生命周期的能力顽分,然后通過
Lifecycle#addObserver()
添加觀察者即可賦予其他對象這個觀察能力,如下示例:// 作為觀察者施蜜,我們需要實現(xiàn) LifecycleObserver 接口 public class MyObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) // 在onResume時執(zhí)行 public void connectListener() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) // 在onPause時執(zhí)行 public void disconnectListener() { ... } } // 添加一個觀察者卒蘸,使得這個觀察者也可以監(jiān)聽組件狀態(tài)變化 myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifeOwner
的概念
LifeOwner
只包含一個getLifecycle()
方法,用于獲取Lifecycle
花墩,使用時必須實現(xiàn)這個方法悬秉。如果想要管理整個應(yīng)用進程的生命周期,可以使用ProcessLifecycleOwner
代替冰蘑。這個接口從
Activity
和Fragment
等中抽取了Lifecycle
的所有權(quán)和泌,并且允許編寫組件來與之配合,任何自定義的應(yīng)用類都可以實現(xiàn)LifecOwner
接口祠肥。實現(xiàn)了
LifecycleOwner
的組件與實現(xiàn)了LifecycleObserver
的組件運作方式是無縫銜接的的武氓,因為Owner
用于提供事件,而Observer
用于注冊、監(jiān)聽事件县恕。
-
在前面我們定義了一個實現(xiàn)了
LifecycleObserver
接口的MyLocationLIstener
類东羹,我們可以如下面代碼這樣在onCreate
中初始化,這意味響應(yīng)生命周期變化的邏輯都提取到了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
的不合適狀態(tài)下執(zhí)行回調(diào)美尸,比如如果這個回調(diào)用于在Activity
保存狀態(tài)后執(zhí)行Fragment
的轉(zhuǎn)場切換冤议,就會觸發(fā)崩潰,因此我們千萬不要執(zhí)行這個回調(diào)师坎。為了簡單處理這個問題恕酸,Lifecycle
允許其他對象查看當(dāng)前狀態(tài)。
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的當(dāng)前狀態(tài) if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
- 通過上面實現(xiàn)胯陋,我們的
MyLocationListener
就完全可以管理生命周期了蕊温,如果想要在其他Activity
或Fragment
中使用它,那么只需要初始化一下就行了遏乔,其他處理操作都會在它內(nèi)部處理义矛。 - 如果一個類庫提供需要結(jié)合
Android
生命周期的處理類,那么建議使用Lifecycle-aware
組件按灶,這樣的話類庫客戶端就可以輕易地整合這些組件而不需要手動地在客戶端處理生命周期管理工作症革。
- 為了避免在
-
實現(xiàn)自定義的 LifecycleOwner
- 在
Support Library 26.1.0
以及上版本中,Fragment
和Activity
已經(jīng)實現(xiàn)了LifecycleOwner
接口鸯旁。 - 如果需要自定義實現(xiàn)一個
LifecycleOwner
噪矛,那么可以使用LifecycleRegistry
類,但是你需要發(fā)送事件到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
的簡潔性,它們不應(yīng)該請求它們自身的數(shù)據(jù)韭赘,而應(yīng)交給ViewModel
去做缩滨,并觀察一個LiveData
對象,用來將變化返回給UI
視圖泉瞻。盡量編寫數(shù)據(jù)驅(qū)動型(
data-driven
)UI
脉漏,這種形式下,UI
控制器只需要負責(zé)在數(shù)據(jù)改變時更新視圖袖牙,或者通知用戶動作給ViewModel
將數(shù)據(jù)邏輯放到
ViewModel
類侧巨,ViewModel
應(yīng)當(dāng)用作UI
控制器和應(yīng)用其他部分的連接器,但是注意鞭达,ViewModel
不負責(zé)請求數(shù)據(jù)(比如網(wǎng)絡(luò)請求等)司忱,相反皇忿,它只是調(diào)用數(shù)據(jù)請求模塊去請求數(shù)據(jù),然后將數(shù)據(jù)結(jié)果返回給UI
控制器坦仍。使用
DataBinding
來維持視圖與UI
控制器間的簡潔性鳍烁。它可以可簡化視圖的聲明和視圖更新時所需在UI
控制器中編寫的代碼,如果喜歡使用Java
繁扎,那么建議使用類似于ButterKnife
之類的類庫來避免編寫無聊的聲明代碼幔荒,并且它可以實現(xiàn)更好的抽象。如果
UI
很復(fù)雜锻离,可以考慮創(chuàng)建一個Presenter
類來處理UI
更改操作铺峭,這可能很費事,但可以使UI
組件更易于測試汽纠。禁止在
ViewModel
中引用View
或者Activity
上下文(context
)震束,否則如果ViewModel
生命周期比Activity
長時(比如configuration change
的情況)陶舞,Activity
就會內(nèi)存泄露而不被GC
了。
lifecycle-aware 組件使用場景
lifecycle-aware
組件可以在各種場景中讓生命周期的管理更簡單豌拙,比如以下場景:
-
粗略定位(
coarse-grained
)與高精度定位(fine-grained
)之間的更新狀態(tài)切換钓账。使用lifecycle-aware
組件在應(yīng)用處于前臺時開啟高精度定位碴犬,而在后臺時開啟粗略定位,可以結(jié)合LiveData
來實現(xiàn)狀態(tài)改變時更新UI
的操作梆暮。 -
開啟和關(guān)閉視頻緩沖服协。 比如使用
lifecycle-aware
組件盡快開啟視頻緩沖,而延遲到應(yīng)用完全啟動后才真正播放視頻啦粹,同樣也可以在應(yīng)用關(guān)閉時終止緩沖動作偿荷。 -
開啟和關(guān)閉網(wǎng)絡(luò)連接。 使用
lifecycle-aware
組件進行動態(tài)更新網(wǎng)絡(luò)數(shù)據(jù)唠椭,如應(yīng)用處于前臺時自動加載數(shù)據(jù)跳纳,而應(yīng)用切換至后臺時自動暫停加載。 -
啟動和暫停
Drawable
動畫贪嫂。 前臺時播放動畫寺庄,后臺是暫停動畫。
處理 onStop 事件
當(dāng)Lifecycle
關(guān)聯(lián)到AppCompatActivity
或Fragment
時力崇,它的狀態(tài)會切換到CREATED
斗塘,而ON_STOP
狀態(tài)則是會在AppCompatActivity
或Fragment
的onSaveInstanceState()
被調(diào)用是觸發(fā)。
如果AppCompatActivity
或Fragment
是通過onSaveInstanceState()
中保存狀態(tài)的亮靴,那么在ON_START
被調(diào)用之前馍盟,它們的UI
狀態(tài)都會被認定為不可變的(immutable
)。這時如果嘗試在UI
狀態(tài)保存后修改UI
的話台猴,就會導(dǎo)致應(yīng)用導(dǎo)航狀態(tài)不一致朽合,這也就是為什么在狀態(tài)保存后執(zhí)行FragmentTransaction
俱两,FragmentManager
會拋異常的原因了,具體看 commit()方法曹步。
如果LiveData
的已經(jīng)關(guān)聯(lián)到Lifecycle
的Observer
還沒到到達STARTED
狀態(tài)的話, LiveData
可以通過終止observer
的調(diào)用來避免上述邊角情況的發(fā)生宪彩,這是因為LiveData
會在執(zhí)行Observer
之前先調(diào)用isAtLeast()
確定狀態(tài),然后再決定是否執(zhí)行讲婚。
然而不幸的是尿孔,AppCompatActivity
的onStop()
方法實在onSaveInstanceState()
之后調(diào)用的,這種情況就導(dǎo)致已經(jīng)保存的UI
狀態(tài)不允許改變筹麸,而Lifecycle
又還沒有到達STARTED
狀態(tài)活合。
為了避免這個問題的發(fā)生,在版本beta2
及之前的Lifecycle
類都會將這一狀態(tài)標記為CREATED
物赶,而不分發(fā)這一事件白指,這樣,任何檢查當(dāng)前狀態(tài)的代碼都能拿到真實狀態(tài)值酵紫,即使這一事件還沒有被分發(fā)告嘲,直到系統(tǒng)調(diào)用onStop()
方法。
然而又不幸的是奖地,這個解決方案有兩大問題:
- 在
API 23
及之前的版本橄唬,Android
系統(tǒng)確實會保存Activity
的狀態(tài),即使是由其他AActivity
轉(zhuǎn)換的部分参歹,也就是說仰楚,系統(tǒng)調(diào)用onSaveInstanceState()
,但是確實沒有調(diào)用onStop()
必要犬庇。這造成了一個潛在的長間隔期僧界,而在這個間隔期之間,observer
一直會認為Lifecycle
是活動的械筛,即使UI
狀態(tài)已經(jīng)不能被改變了捎泻。 - 任何想要暴露給
LiveData
類似行為的類都必須實現(xiàn)Lifecycle
在beta2
及之前版本所提供的解決方案。
Note: 為了簡化流程并兼容老版本埋哟,請直接從版本1.0.0-rc1
開始使用笆豁,Lifecycle
對象會被標記為CREATED
,并且會在onSaveInstanceState()
被調(diào)用是標記為ON_STOP
狀態(tài)赤赊,而無需等待onStop()
的調(diào)用闯狱。雖然這并不會影響我們的代碼,但是確是我們需要注意的抛计,因為它沒有遵循API 26
及以前版本中Activity
的生命周期調(diào)用次序