目錄
-
1. 生命周期感知
- 1.1 生命周期感知組件
- 1.2 LifecycleOwner 的狀態(tài)和事件模型
-
2. LiveData 與 LifecycleOwner 的雙向訂閱
- 2.1 LiveData 訂閱生命周期變化
- 2.2 LifecycleOwner 訂閱數(shù)據(jù)變化
- 2.3 多對多的雙向訂閱網(wǎng)
- 3 LiveData 的事件變化
-
4 LifecycleOwner 的事件變化
- 4.1 Lifecycle 接口的實(shí)現(xiàn)——LifecycleRegistry
- 4.1.1 LifecycleRegistry 的訂閱實(shí)現(xiàn)
- 4.1.2 LifecycleRegistry 中的事件流
- 4.1.3 處理生命周期的變化
- 4.1 Lifecycle 接口的實(shí)現(xiàn)——LifecycleRegistry
- 5. 關(guān)于觀察者模式的一點(diǎn)思考
1. 生命周期感知
1.1 生命周期感知組件
我們知道合武,Controller(Activity or Fragment) 都是有生命周期的撕阎,但是傳統(tǒng)的 Controller 實(shí)現(xiàn)方式只負(fù)責(zé) Controller 本身的生命周期管理,而與業(yè)務(wù)層的數(shù)據(jù)之間并沒有實(shí)現(xiàn)良好解耦的生命周期事件交換轮纫。所以業(yè)務(wù)層都需要自己主動去感知 Controller 生命周期的變化趁仙,并在 Controller 的生存期處理數(shù)據(jù)的焙樘恚活,而在消亡時刻解除與 Controller 之間的關(guān)系雀费,這種處理方式隨著業(yè)務(wù)規(guī)模的擴(kuò)大往往顯得代碼臃腫和難以維護(hù)薇组。
Jetpack 框架讓 Controller 變得可感知,成為一個生命周期事件變化的通知中心坐儿,我們實(shí)現(xiàn)的任何組件或?qū)ο蠖伎梢酝ㄟ^訂閱這個通知中心實(shí)時知道 Controller 生命周期的變化(這樣的組件或?qū)ο笪覀兎Q之為生命周期感知組件),而不需要繁瑣的主動詢問宋光;同時貌矿,Jetpack 推薦將數(shù)據(jù)封裝成 LiveData,因?yàn)?LiveData 自帶感知 Controller 的生命周期變化罪佳,自我維護(hù)數(shù)據(jù)更新與生命周期更新的協(xié)調(diào)關(guān)系逛漫。LiveData 是典型的生命周期感知組件。而現(xiàn)在的 Controller 我們稱之為生命周期可感知 Controller赘艳,在 Jetpack 的實(shí)現(xiàn)中酌毡,有一個專門的接口來代表它——LifecycleOwner,名副其實(shí)蕾管,Controller 是生命周期的所有者枷踏。
1.2 LifecycleOwner 的狀態(tài)和事件模型
LifecycleOwner 中維護(hù)一個叫做 Lifecycle 的接口,它規(guī)定了生命周期的狀態(tài)和狀態(tài)切換的事件流模型掰曾。在 android developer 的官方文檔中旭蠕,給出了一個非常清晰的時序圖來說明這個模型:
- 生命周期的狀態(tài)總共有 5 個:DESTROYED,INITIALIZED,CREATED掏熬,STARTED佑稠,RESUMED;
- 狀態(tài)切換事件總共有 7 個:ON_CREATE旗芬,ON_START舌胶,ON_RESUME,ON_PAUSE疮丛,ON_STOP幔嫂,ON_DESTROY,ON_ANY这刷;
- 每個事件除了 ON_ANY 以外婉烟,都嚴(yán)格在 Controller 的 onXXX() 回調(diào)中產(chǎn)生,比如 ON_CREATE 事件在 Activity.onCreate() 和 Fragment.onCreate() 中進(jìn)行分派暇屋;
- 需要注意 CREATED 和 STARTED 兩個狀態(tài)的入度都為 2似袁,即有兩個不同的事件都能達(dá)到這個狀態(tài)。
2. LiveData 與 LifecycleOwner 的雙向訂閱
在講述 LiveData 的原理時咐刨,沒有辦法孤立地談 LiveData昙衅,因?yàn)樗膶?shí)現(xiàn)和 LifecycleOwner 的交互是分不開的,所以這里需要將兩者結(jié)合進(jìn)行說明定鸟。
2.1 LiveData 訂閱生命周期變化
LiveData 作為 View 的 UI 狀態(tài)數(shù)據(jù)源而涉,并不是在 LifecycleOwner 的每個生命周期狀態(tài)中都可用的,而必須在 View 完成所有的測量和布局操作后联予,才能基于 LiveData 進(jìn)行 UI 狀態(tài)更新啼县。這說明 LiveData 是有一個可用狀態(tài)標(biāo)記的,在源代碼中沸久,標(biāo)記為 active:
LiveData 中更新 active 標(biāo)記的方法:
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
這說明季眷,只有當(dāng) LifecycleOwner 的狀態(tài)至少是 STARTED,LiveData 才是處于激活狀態(tài)的卷胯。再看 Lifecycle.State 的枚舉順序:
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
/**
* Compares if this State is greater or equal to the given {@code state}.
*
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
*/
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
進(jìn)一步說明子刮,只有當(dāng) LifecycleOwner 的狀態(tài)是 STARTED 和 RESUMED 時,LiveData 才是處于激活狀態(tài)的窑睁,而只有在激活狀態(tài)下挺峡,LiveData 才會將最新數(shù)據(jù)變化通知給它的訂閱者:
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) { // 沒有激活,不進(jìn)行通知
return;
}
// 在 LifecycleOwner 的生命周期變化事件分派之前担钮,需要提前主動更新一下激活狀態(tài)橱赠,
// 如果未激活,同樣不進(jìn)行通知
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//...省略非關(guān)鍵代碼
observer.mObserver.onChanged((T) mData);
}
嚴(yán)格的說這里并不應(yīng)該叫 LiveData 的激活狀態(tài)箫津,而應(yīng)該是向 LiveData 進(jìn)行訂閱的 LifecycleOwner 的激活狀態(tài)病线,此時 LifecycleOwner 作為觀察者觀察 LiveData 的變化吓著。所以這里可能叫 LiveData 在每一個 LifecycleOwner 上的分身的激活狀態(tài)更合適,為了表述方便送挑,我們就統(tǒng)稱叫 LiveData 的激活狀態(tài)绑莺。我們將在 2.2 節(jié)描述 LifecycleOwner 如何訂閱 LiveData。
以上惕耕,只為了說明一個問題:LiveData 需要訂閱 LifecycleOwner纺裁,感知其生命周期變化:
圖示說明,LiveData 訂閱 LifecycleOwner司澎,而由 LifecycleOwner.Lifecycle 代理完成生命周期狀態(tài)變化通知欺缘,所以 LiveData 直接能感知的是 Lifecycle。
2.2 LifecycleOwner 訂閱數(shù)據(jù)變化
LifecycleOwner 在 STARTED 和 RESUMED 的狀態(tài)下可以根據(jù) LiveData 更新 UI 的狀態(tài)挤安,所以 LifecycleOwner 需要訂閱 LiveData 的數(shù)據(jù)變化谚殊。
在實(shí)際實(shí)現(xiàn)當(dāng)中,LifecycleOwner 作為抽象層并不具體負(fù)責(zé)訂閱 LiveData蛤铜,而是由業(yè)務(wù)層在 LifecycleOwner 中完成具體的訂閱工作嫩絮,此時我們稱 LifecycleOwner 為 Controller 更合適,雖然它們往往是同一個東西:
注意圖示围肥,一個 User-defined Observer 必須和一個 LifecycleOwner 唯一綁定剿干,否則將無法訂閱。試想穆刻,如果一個 Observer 同時綁定兩個 LifecycleOwner:L1 和 L2置尔,假如 L1 處于 RESUMED 的狀態(tài),而 L2 處于 DESTROYED 的狀態(tài)氢伟,那么 LiveData 將無所適從:如果遵循 L1 的狀態(tài)榜轿,將變化通知給 Observer,則更新 L2 會出錯朵锣;如果遵循 L2 的狀態(tài)谬盐,不將變化通知給 Observer,則 L1 得不到及時更新猪勇。
2.3 多對多的雙向訂閱網(wǎng)
LiveData 和 LifecycleOwner 之間因?yàn)樾枰嗷ビ^察對方狀態(tài)的變化,從而需要實(shí)現(xiàn)雙向訂閱颠蕴;同時泣刹,為了支持良好的可擴(kuò)展能力,各自都維護(hù)了一個觀察者列表犀被,形成一個多對多的雙向訂閱網(wǎng)絡(luò):
我們看到一個 LiveData 是可以同時向多個 LifecycleOwner 發(fā)起訂閱的椅您,所以,LiveData 本身其實(shí)并不實(shí)際維護(hù)一個激活狀態(tài)寡键,真正的激活狀態(tài)維護(hù)在 LifecycleOwner 的 User-defined observer 中掀泳。
3 LiveData 的事件變化
LiveData 值更新之后的需要通知訂閱者(觀察者),其通知流程非常簡單:
其中,判斷觀察者是否激活员舵,即判斷 LifecycleOwner 是否處于 STARTED 或 RESUMED 狀態(tài)脑沿,在 2.1 節(jié)中已有說明。
我們看一下關(guān)鍵的源代碼:
// 入口
@MainThread
protected void setValue(T value) {
// 必須在主線程調(diào)用
assertMainThread("setValue");
//..省略非關(guān)鍵代碼
// 設(shè)置新值并派發(fā)通知
mData = value;
dispatchingValue(null);
}
// 通知派發(fā)流程
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//..省略非關(guān)鍵代碼
// 遍歷觀察者列表
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// 嘗試通知觀察者
considerNotify(iterator.next().getValue());
//..省略非關(guān)鍵代碼
}
}
其中 LiveData.considerNotify() 在 2.1 節(jié)中已有說明马僻。
4 LifecycleOwner 的事件變化
對于 LifecycleOwner 來說庄拇,其變化的事件即為生命周期狀態(tài)的變化。在 LifecycleOwner 的事件委托者 Lifecycle 看來韭邓,無論是發(fā)生了 ON_CREATE 事件還是 ON_START 事件措近,或是任何其它的事件,其事件的切換流程都是通用的女淑。
換言之瞭郑,只要 Lifecycle 接口的實(shí)現(xiàn)者實(shí)現(xiàn)這一通用切換流程,便只需給 LifecycleOwner 暴露一個切換入口鸭你,就能在 LifecycleOwner 的各個生命周期回調(diào)函數(shù)中調(diào)用這個入口就可以了屈张。這樣我們在 LifecycleOwner 中應(yīng)該可以看到形如這樣的流程(偽代碼表示):
public class Activity/Fragment implements LifecycleOwner {
@Override
public onCrate() {
//...省略非關(guān)鍵代碼
// 在 Jetpack 框架中,LifecycleImpl 被命名為 LifecycleRegistry
LifecycleImpl.handleLifecycleEvent(ON_CREATE);
}
@Override
public onStart() {
//...省略非關(guān)鍵代碼
LifecycleImpl.handleLifecycleEvent(ON_START);
}
@Override
public onResume() {
//...省略非關(guān)鍵代碼
LifecycleImpl.handleLifecycleEvent(ON_RESUME);
}
@Override
public onPause() {
//...省略非關(guān)鍵代碼
LifecycleImpl.handleLifecycleEvent(ON_PAUSE);
}
@Override
public onDestroy() {
//...省略非關(guān)鍵代碼
LifecycleImpl.handleLifecycleEvent(ON_DESTROY);
}
}
當(dāng)然苇本,在具體的源代碼中袜茧,與上述偽代碼會有一些出入,但是大體的結(jié)構(gòu)是一致的瓣窄。在 Jetpack 框架中笛厦,這個 Lifecycle 的實(shí)現(xiàn)者叫做 LifecycleRegistry。所以我們這里重點(diǎn)需要關(guān)注的就是 LifecycleRegistry 這個 Lifecycle 的代理接口的實(shí)現(xiàn)類是如何通知生命周期事件變化的俺夕。
4.1 Lifecycle 接口的實(shí)現(xiàn)——LifecycleRegistry
4.1.1 LifecycleRegistry 的訂閱實(shí)現(xiàn)
如 2.2 節(jié)所述裳凸,通過 LiveData.observe(owner, user-defined observer),LifecycleOwner 的業(yè)務(wù)層向 LiveData 訂閱數(shù)據(jù)變化劝贸,而在 LiveData.observe() 方法內(nèi)姨谷,同時會自動通過 Lifecycle.addObserver(LiveData-defined observer) 向 LifecycleOwner 訂閱生命周期變化:
// LiveData.observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//...省略非關(guān)鍵代碼
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//...省略非關(guān)鍵代碼
// 向 LifecycleOwner 發(fā)起訂閱
owner.getLifecycle().addObserver(wrapper);
}
以上方法內(nèi)的 owner.getLifecycle() 的實(shí)際對象即為 LifecycleRegistry,我們來看一下 LifecycleRegistry.addObserver() 的基本訂閱流程:
從整個流程來看映九,總體可以分為三步:
- 第一步是初始化觀察者對象的狀態(tài)梦湘,并將觀察者緩存入隊;
- 第二步是以模擬派發(fā)生命周期事件的形式件甥,將新加入的觀察者的狀態(tài)提升到目前為止可提升的最大狀態(tài)捌议;
- 第三步是同步所有觀察者的狀態(tài)到全局狀態(tài)。
我們可以看到引有,最后所有的觀察者的狀態(tài)都要同步到全局狀態(tài)瓣颅,全局狀態(tài)即為 LifecyclerOwner 最新的狀態(tài)。那么為什么需要進(jìn)行這么繁瑣的逐步模擬派發(fā)事件來進(jìn)行同步呢譬正?直接一步到位不行么宫补?
我們可以考慮一個生命周期感知組件 LifeLocation檬姥,其功能是用于進(jìn)行定位,它訂閱了 LifecycleOwner粉怕,我們假設(shè) LifeLocation 需要在 CREATED 的時候進(jìn)行一些必要的初始化健民,而在 STARTED 的時候開始執(zhí)行定位操作。假如在 LifecycleRegistry 中的狀態(tài)同步可以一步同步到全局狀態(tài)斋荞,那么有可能當(dāng)前的全局狀態(tài)已經(jīng)是 RESUMED 的了荞雏,這樣 LifeLocation 既得不到初始化,也無從啟用定位功能了平酿。
所以凤优,以上這種看似繁瑣的模擬派發(fā)狀態(tài)事件的步驟是完全必要的,它讓用戶自定義的生命周期感知組件的狀態(tài)切換流程是可預(yù)測的蜈彼。
4.1.2 LifecycleRegistry 中的事件流
我們在 4.1.1 節(jié)中的流程圖的第 6 步中提到筑辨,要根據(jù) observer.state 來計算下一個狀態(tài)事件,也就是說按照事件的流向幸逆,根據(jù)當(dāng)前的狀態(tài)棍辕,下一個要發(fā)生的事件是什么。我們修改一下 1.2 節(jié)的時序圖如下:
觀察到圖中左邊的藍(lán)色箭頭还绘,舉個例子楚昭,假如當(dāng)前的狀態(tài)是 CREATED,那么接下來要發(fā)生的事件應(yīng)該是 ON_START拍顷。藍(lán)色箭頭指示的事件流方向是生命周期由無到生的過程抚太,我們稱為 upEvent 流;與此對應(yīng)昔案,右邊的紅色箭頭指示的事件流方向是生命周期由生到死的過程尿贫,我們稱之為 downEvent。
4.1.1 節(jié)中的流程圖的第 6 步中正好需要進(jìn)行 upEvent 流操作踏揣。除此以外庆亡,我們在第 7 步同步到全局狀態(tài)時,還需要用到 upEvent 和 downEvent 流操作捞稿,且在 LifecycleOwner 的每一次生命周期的變化中又谋,都需要進(jìn)行上述第 7 步的狀態(tài)同步操作。接下來我們就看一看娱局,當(dāng) LifecycleOwner 生命周期變化后彰亥,發(fā)生了什么。
4.1.3 處理生命周期的變化
在 4 節(jié)開頭我們描述了 LifecycleImpl.handleLifecycleEvent() 方法铃辖,在 LifecycleRegistry 中也有一個同名的方法剩愧,其功能就是處理 LifecycleOwner 生命周期的變化猪叙。handleLifecycleEvent() 的處理過程是這樣的:
如圖所示:
- Sync 標(biāo)記的部分是進(jìn)行狀態(tài)同步的核心流程娇斩,同時也是 4.1.1 節(jié)流程圖中的第 7 步的具體實(shí)現(xiàn)仁卷;
- 每一次生命周期的變化有可能是從無到生的 up 變化,也有可能是從生到死的 down 變化犬第;
- 如果是 up 變化锦积,則需要進(jìn)行 upEvent 流處理,如果是 down 變化歉嗓,則需要進(jìn)行 downEvent 流處理丰介;
- 根據(jù) 4.1.1 節(jié)的描述,我們可以得出鉴分,在觀察者隊列中的所有觀察者哮幢,從最老(最開始)到最新(最末),必定維持一個不變性質(zhì):非降序排列志珍;
- 所以當(dāng) STATE < eldestState 時橙垢,說明觀察者隊列中的所有觀察者狀態(tài)都大于全局狀態(tài),這時候說明生命周期變化順序是 down 方向的伦糯,需要進(jìn)行 downEvent 流處理柜某;
- 而當(dāng) STATE > newestState 時,說明觀察者隊列中的所有觀察者狀態(tài)都小于全局狀態(tài)敛纲,這時候說明生命周期變化順序是 up 方向的喂击,需要進(jìn)行 upEvent 流處理;
- 無論是 downEvent 流還是 upEvent 流淤翔,都會逐步派發(fā)生命周期事件給各個觀察者翰绊。
關(guān)于 downEvent 流和 upEvent 流,我畫了一張更加形象的圖用以加深理解:
至此办铡,整個 LiveData 和 Lifecycle 的原理就介紹完成了辞做。
5. 關(guān)于觀察者模式的一點(diǎn)思考
不難看出,LiveData 和 Lifecycle 的核心是觀察者模式寡具。無論是 LiveData 還是 Lifecycle秤茅,它們的共同點(diǎn)就是都需要維護(hù)一個穩(wěn)定的狀態(tài)機(jī):
- LiveData 的狀態(tài)機(jī)就是數(shù)據(jù)值的變化,每個值就是一個狀態(tài)童叠,理論上可以是一個無限狀態(tài)機(jī)框喳;
- Lifecycle 的狀態(tài)機(jī)就是生命周期的變化,每個生命周期階段就是一個狀態(tài)厦坛,它是一個有限狀態(tài)機(jī)五垮。
在涉及到狀態(tài)機(jī)模型時,如果我們需要感知狀態(tài)機(jī)當(dāng)前的狀態(tài)杜秸,一般有兩種方式:主動詢問和被動通知放仗。在復(fù)雜的業(yè)務(wù)中,主動詢問狀態(tài)機(jī)往往是不好的實(shí)踐撬碟;而被動通知诞挨,可以讓我們的業(yè)務(wù)按照狀態(tài)進(jìn)行清晰的分段莉撇,更易于模塊化和測試。觀察者模式就是一種很好的被動通知模式惶傻。
所以棍郎,當(dāng)我們的對象維護(hù)了一個狀態(tài)機(jī)的時候,可以考慮是否可以采用觀察者模式來讀取狀態(tài)银室。但是需要注意的是涂佃,觀察者模式內(nèi)部是維護(hù)了一個觀察者引用的列表的,當(dāng)狀態(tài)發(fā)生變化的時候蜈敢,是采用順序遍歷的方式逐個進(jìn)行通知的辜荠,可以想到,當(dāng)一個被觀察者中維護(hù)的觀察者數(shù)量很多抓狭,其中又有很多觀察者對狀態(tài)的響應(yīng)處理都比較耗時的話侨拦,會出現(xiàn)性能瓶頸。尤其是在基于單線程的 UI 環(huán)境下辐宾,更加需要引起注意狱从,我們通常應(yīng)該有一個機(jī)制來移除不再需要的觀察者,以減輕通知負(fù)載叠纹。
說明:
該文檔參考的 androidx 版本為
core: 1.1.0
lifecyle: 2.2.0-alpha01
fragment: 1.1.0-alpha09
如果大家喜歡我寫的文章季研,歡迎關(guān)注我的公眾號——小舍,將有更多有趣的內(nèi)容分享給大家