Android Jetpack架構(gòu)篇:Lifecycles
使用生命周期感知組件處理生命周期
生命周期感知組件執(zhí)行操作以響應(yīng)另一個(gè)組件(例如活動(dòng)和片段)的生命周期狀態(tài)的更改。這些組件可幫助您生成更易于組織且通常更輕量級(jí)的代碼,這些代碼更易于維護(hù)寸认。
一種常見的模式是在活動(dòng)和片段的生命周期方法中實(shí)現(xiàn)依賴組件的操作。但是盾戴,這種模式導(dǎo)致代碼組織不良以及錯(cuò)誤的增加油吭。通過使用生命周期感知組件勾扭,您可以將依賴組件的代碼移出生命周期方法并移入組件本身矫钓。
該android.arch.lifecycle 包提供了類和接口浮禾,使您可以構(gòu)建生命周期感知 組件 - 這些組件可以根據(jù)活動(dòng)或片段的當(dāng)前生命周期狀態(tài)自動(dòng)調(diào)整其行為。
注意: 要導(dǎo)入 android.arch.lifecycle Android項(xiàng)目份汗,請(qǐng)參閱向項(xiàng)目添加組件。
Android框架中定義的大多數(shù)應(yīng)用程序組件都附加了生命周期蝴簇。生命周期由操作系統(tǒng)或流程中運(yùn)行的框架代碼管理杯活。它們是Android工作原理的核心,您的應(yīng)用程序必須尊重它們熬词。不這樣做可能會(huì)觸發(fā)內(nèi)存泄漏甚至應(yīng)用程序崩潰旁钧。
想象一下,我們有一個(gè)活動(dòng)互拾,在屏幕上顯示設(shè)備位置歪今。常見的實(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
}
}
即使這個(gè)示例看起來很好,但在真實(shí)的應(yīng)用程序中颜矿,最終會(huì)有太多的調(diào)用來管理UI和其他組件以響應(yīng)生命周期的當(dāng)前狀態(tài)寄猩。管理多個(gè)組件會(huì)在生命周期方法中放置大量代碼,例如 onStart() 和 onStop()骑疆,這使得它們難以維護(hù)田篇。
此外,無法保證組件在活動(dòng)或片段停止之前啟動(dòng)箍铭。如果我們需要執(zhí)行長(zhǎng)時(shí)間運(yùn)行的操作(例如某些配置檢入)泊柬,則尤其如此onStart()。這可能導(dǎo)致競(jìng)爭(zhēng)條件诈火,其中onStop()方法在之前完成onStart()兽赁,使組件保持活動(dòng)的時(shí)間長(zhǎng)于其所需的時(shí)間。
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 是一個(gè)類,它包含有關(guān)組件生命周期狀態(tài)的信息(如活動(dòng)或片段)拍摇,并允許其他對(duì)象觀察此狀態(tài)蒲跨。
Lifecycle 使用兩個(gè)主要枚舉來跟蹤其關(guān)聯(lián)組件的生命周期狀態(tài):
Event(事件)
從框架和Lifecycle類調(diào)度的生命周期事件 。這些事件映射到活動(dòng)和片段中的回調(diào)事件授翻。
State(狀態(tài))
Lifecycle對(duì)象跟蹤的組件的當(dāng)前狀態(tài) 或悲。
將狀態(tài)視為圖形的節(jié)點(diǎn)孙咪,將事件視為這些節(jié)點(diǎn)之間的邊緣。
類可以通過向其方法添加注釋來監(jiān)視組件的生命周期狀態(tài)巡语。然后翎蹈,您可以通過調(diào)用 類的 addObserver() 方法Lifecycle并傳遞觀察者的實(shí)例來添加觀察者,如以下示例所示:
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對(duì)象實(shí)現(xiàn)了 LifecycleOwner 接口荤堪,將在下一節(jié)中進(jìn)行說明。
LifecycleOwner
LifecycleOwner 是一個(gè)單一的方法接口枢赔,表示該類有一個(gè) Lifecycle澄阳。它有一個(gè)方法, getLifecycle() 必須由類實(shí)現(xiàn)踏拜。如果您正在嘗試管理整個(gè)應(yīng)用程序流程的生命周期碎赢,請(qǐng)參閱 ProcessLifecycleOwner。
此接口Lifecycle從各個(gè)類(例如Fragment和)中抽象出所有權(quán) AppCompatActivity速梗,并允許編寫與其一起使用的組件肮塞。任何自定義應(yīng)用程序類都可以實(shí)現(xiàn)LifecycleOwner 接口。
與實(shí)現(xiàn)的組件LifecycleObserver 無縫實(shí)現(xiàn) 工作的組件姻锁, LifecycleOwner 因?yàn)樗姓呖梢蕴峁┥芷谡碚裕^察者可以注冊(cè)觀察。
對(duì)于位置跟蹤的例子位隶,我們可以使MyLocationListener類實(shí)現(xiàn)LifecycleObserver 拷窜,然后用活動(dòng)的初始化 Lifecycle的onCreate()方法。這允許 MyLocationListener類自給自足涧黄,這意味著響應(yīng)生命周期狀態(tài)變化的邏輯被聲明MyLocationListener而不是活動(dòng)装黑。使各個(gè)組件存儲(chǔ)自己的邏輯使得活動(dòng)和片段邏輯更易于管理。
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();
}
});
}
}
一個(gè)常見的用例是弓熏,如果Lifecycle現(xiàn)在不處于良好狀態(tài)恋谭,則應(yīng)避免調(diào)用某些回調(diào) 。例如挽鞠,如果回調(diào)在保存活動(dòng)狀態(tài)后運(yùn)行片段事務(wù)疚颊,則會(huì)觸發(fā)崩潰,因此我們永遠(yuǎn)不會(huì)想要調(diào)用該回調(diào)信认。
為了簡(jiǎn)化這個(gè)用例材义,Lifecycle該類允許其他對(duì)象查詢當(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;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
通過這種實(shí)現(xiàn)嫁赏,我們的LocationListener類完全可以識(shí)別生命周期其掂。如果我們需要使用LocationListener另一個(gè)活動(dòng)或片段,我們只需要初始化它潦蝇。所有設(shè)置和拆卸操作都由類本身管理款熬。
如果庫提供了需要使用Android生命周期的類深寥,我們建議您使用生命周期感知組件。您的庫客戶端可以輕松地集成這些組件贤牛,而無需在客戶端進(jìn)行手動(dòng)生命周期管理惋鹅。
實(shí)現(xiàn)自定義LifecycleOwner
支持庫26.1.0及更高版本中的片段和活動(dòng)已實(shí)現(xiàn)該LifecycleOwner 接口。
如果您要?jiǎng)?chuàng)建自定義類殉簸,則 LifecycleOwner可以使用 LifecycleRegistry 類闰集,但需要將事件轉(zhuǎn)發(fā)到該類,如以下代碼示例所示:
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;
}
}
生命周期感知組件的最佳實(shí)踐
保持UI控制器(活動(dòng)和片段)盡可能精簡(jiǎn)般卑。他們不應(yīng)該試圖獲取自己的數(shù)據(jù); 相反武鲁,使用a ViewModel執(zhí)行此操作,并觀察LiveData 對(duì)象以將更改反映回視圖蝠检。
嘗試編寫數(shù)據(jù)驅(qū)動(dòng)的UI沐鼠,您的UI控制器負(fù)責(zé)在數(shù)據(jù)更改時(shí)更新視圖,或者將用戶操作通知給 ViewModel蝇率。
把你的數(shù)據(jù)邏輯放在你的 ViewModel班級(jí)。 ViewModel應(yīng)該作為UI控制器和應(yīng)用程序其余部分之間的連接器刽沾。但要小心本慕,ViewModel獲取數(shù)據(jù)(例如,從網(wǎng)絡(luò))是沒有 責(zé)任的侧漓。相反锅尘, ViewModel應(yīng)該調(diào)用適當(dāng)?shù)慕M件來獲取數(shù)據(jù),然后將結(jié)果提供回UI控制器布蔗。
使用數(shù)據(jù)綁定來維護(hù)視圖和UI控制器之間的干凈界面藤违。這使您可以使視圖更具聲明性,并最大限度地減少在活動(dòng)和片段中編寫所需的更新代碼纵揍。如果您更喜歡使用Java編程語言執(zhí)行此操作顿乒,請(qǐng)使用Butter Knife之類的庫 來避免樣板代碼并具有更好的抽象。
如果您的UI很復(fù)雜泽谨,請(qǐng)考慮創(chuàng)建一個(gè) presenter 類來處理UI修改璧榄。這可能是一項(xiàng)艱巨的任務(wù),但它可以使您的UI組件更容易測(cè)試吧雹。
避免引用 你的內(nèi)容View或Activity上下文ViewModel骨杂。如果ViewModel活動(dòng)超過活動(dòng)(在配置更改的情況下),您的活動(dòng)將泄漏并且垃圾收集器未正確處理雄卷。
生命周期感知組件的用例
生命周期感知組件可以使您在各種情況下更輕松地管理生命周期搓蚪。一些例子是:
- 在粗粒度和細(xì)粒度位置更新之間切換。使用生命周期感知組件可在您的位置應(yīng)用程序可見時(shí)啟用細(xì)粒度位置更新丁鹉,并在應(yīng)用程序位于后臺(tái)時(shí)切換到粗粒度更新妒潭。LiveData悴能,一個(gè)生命周期感知組件,允許您的應(yīng)用在用戶更改位置時(shí)自動(dòng)更新UI杜耙。
- 停止并開始視頻緩沖搜骡。使用生命周期感知組件盡快啟動(dòng)視頻緩沖,但推遲播放直到應(yīng)用程序完全啟動(dòng)佑女。您還可以使用生命周期感知組件在銷毀應(yīng)用程序時(shí)終止緩沖记靡。
- 啟動(dòng)和停止網(wǎng)絡(luò)連接。使用生命周期感知組件在應(yīng)用程序處于前臺(tái)時(shí)啟用網(wǎng)絡(luò)數(shù)據(jù)的實(shí)時(shí)更新(流式傳輸)团驱,并在應(yīng)用程序進(jìn)入后臺(tái)時(shí)自動(dòng)暫停摸吠。
- 暫停和恢復(fù)動(dòng)畫drawables。當(dāng)應(yīng)用程序在后臺(tái)時(shí)嚎花,使用生命周期感知組件處理暫停動(dòng)畫可繪制的內(nèi)容寸痢,并在應(yīng)用程序位于前臺(tái)后恢復(fù)可繪制內(nèi)容。
處理停止事件
當(dāng)一個(gè)Lifecycle屬于一個(gè)AppCompatActivity 或Fragment紊选,所述Lifecycle的狀態(tài)改變?yōu)?CREATED與所述ON_STOP 事件被調(diào)度時(shí)AppCompatActivity或 Fragment的onSaveInstanceState() 被調(diào) 用啼止。
當(dāng)一個(gè)Fragment或者AppCompatActivity的狀態(tài)被保存時(shí) onSaveInstanceState(),它的UI在ON_START 被調(diào)用之前被認(rèn)為是不可變的 兵罢。保存狀態(tài)后嘗試修改UI可能會(huì)導(dǎo)致應(yīng)用程序的導(dǎo)航狀態(tài)不一致献烦,這就是為什么FragmentManager 在應(yīng)用程序運(yùn)行FragmentTransaction 保存后狀態(tài)時(shí)會(huì)拋出異常的 原因。詳情 commit()請(qǐng)見卖词。
LiveData如果觀察者的關(guān)聯(lián)Lifecycle 不是至少 巩那,則通過避免調(diào)用其觀察者來防止這種邊緣情況開箱即用STARTED。在幕后此蜈,它 isAtLeast() 在決定調(diào)用其觀察者之前調(diào)用即横。
不幸的是,AppCompatActivity 的onStop() 方法被調(diào)用后 onSaveInstanceState()裆赵,留下其中UI狀態(tài)东囚,但不允許更改,但差距 Lifecycle 還沒有移動(dòng)到 CREATED 狀態(tài)战授。
為防止出現(xiàn)此問題舔庶,Lifecycle 版本beta2 和較低的類將狀態(tài)標(biāo)記為 CREATED 不調(diào)度事件,以便檢查當(dāng)前狀態(tài)的任何代碼都獲得實(shí)際值陈醒,即使事件未被調(diào)度惕橙,直到onStop() 系統(tǒng)調(diào)用。
不幸的是钉跷,這個(gè)解決方案有兩個(gè)主要問題
- 在API級(jí)別23和更低級(jí)別弥鹦,Android系統(tǒng)實(shí)際上保存了活動(dòng)的狀態(tài),即使它被另一個(gè)活動(dòng)部分覆蓋。換句話說彬坏,Android系統(tǒng)調(diào)用onSaveInstanceState() 但不一定要調(diào)用onStop()朦促。這會(huì)創(chuàng)建一個(gè)潛在的長(zhǎng)間隔,即使無法修改其UI狀態(tài)栓始,觀察者仍然認(rèn)為生命周期處于活動(dòng)狀態(tài)务冕。
- 任何想要向類公開類似行為的 LiveData類都必須實(shí)現(xiàn)Lifecycleversion beta 2和lower 提供的解決方法 。
注意: 為了使此流更簡(jiǎn)單并提供與舊版本的更好兼容性幻赚,從版本開始1.0.0-rc1禀忆,Lifecycle 對(duì)象被標(biāo)記為CREATED 并ON_STOP 在onSaveInstanceState() 調(diào)用時(shí)調(diào)度,而不等待對(duì)onStop() 方法的調(diào)用落恼。這不太可能影響您的代碼箩退,但您需要注意這一點(diǎn),因?yàn)樗cActivityAPI級(jí)別26及更低級(jí)別的類中的調(diào)用順序不匹配佳谦。
其他資源
Lifecyle感知組件是Android Jetpack 的一部分戴涝。在Sunflower演示應(yīng)用程序中查看它們的使用情況。