Android mvvm之LiveData原理案例詳解
1. 生命周期感知
1.1 生命周期感知組件
我們知道,Controller(Activity or Fragment) 都是有生命周期的,但是傳統的 Controller 實現方式隻負責 Controller 本身的生命周期管理,而與業務層的數據之間並沒有實現良好解耦的生命周期事件交換。所以業務層都需要自己主動去感知 Controller 生命周期的變化,並在 Controller 的生存期處理數據的保活,而在消亡時刻解除與 Controller 之間的關系,這種處理方式隨著業務規模的擴大往往顯得代碼臃腫和難以維護。
Jetpack 框架讓 Controller 變得可感知,成為一個生命周期事件變化的通知中心,我們實現的任何組件或對象都可以通過訂閱這個通知中心實時知道 Controller 生命周期的變化(這樣的組件或對象我們稱之為生命周期感知組件),而不需要繁瑣的主動詢問;同時,Jetpack 推薦將數據封裝成 LiveData,因為 LiveData 自帶感知 Controller 的生命周期變化,自我維護數據更新與生命周期更新的協調關系。LiveData 是典型的生命周期感知組件。而現在的 Controller 我們稱之為生命周期可感知 Controller,在 Jetpack 的實現中,有一個專門的接口來代表它——LifecycleOwner,名副其實,Controller 是生命周期的所有者。
1.2 LifecycleOwner 的狀態和事件模型
LifecycleOwner 中維護一個叫做 Lifecycle 的接口,它規定瞭生命周期的狀態和狀態切換的事件流模型。在 android developer 的官方文檔中,給出瞭一個非常清晰的時序圖來說明這個模型:
- 生命周期的狀態總共有 5 個:DESTROYED,INITIALIZED,CREATED,STARTED,RESUMED;
- 狀態切換事件總共有 7 個:ON_CREATE,ON_START,ON_RESUME,ON_PAUSE,ON_STOP,ON_DESTROY,ON_ANY;
- 每個事件除瞭 ON_ANY 以外,都嚴格在 Controller 的 onXXX() 回調中產生,比如 ON_CREATE 事件在 Activity.onCreate() 和 Fragment.onCreate() 中進行分派;
- 需要註意 CREATED 和 STARTED 兩個狀態的入度都為 2,即有兩個不同的事件都能達到這個狀態。
2. LiveData 與 LifecycleOwner 的雙向訂閱
在講述 LiveData 的原理時,沒有辦法孤立地談 LiveData,因為它的實現和 LifecycleOwner 的交互是分不開的,所以這裡需要將兩者結合進行說明。
2.1 LiveData 訂閱生命周期變化
LiveData 作為 View 的 UI 狀態數據源,並不是在 LifecycleOwner 的每個生命周期狀態中都可用的,而必須在 View 完成所有的測量和佈局操作後,才能基於 LiveData 進行 UI 狀態更新。這說明 LiveData 是有一個可用狀態標記的,在源代碼中,標記為 active:
LiveData 中更新 active 標記的方法:
boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); }
這說明,隻有當 LifecycleOwner 的狀態至少是 STARTED,LiveData 才是處於激活狀態的。再看 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; } }
進一步說明,隻有當 LifecycleOwner 的狀態是 STARTED 和 RESUMED 時,LiveData 才是處於激活狀態的,而隻有在激活狀態下,LiveData 才會將最新數據變化通知給它的訂閱者:
private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { // 沒有激活,不進行通知 return; } // 在 LifecycleOwner 的生命周期變化事件分派之前,需要提前主動更新一下激活狀態, // 如果未激活,同樣不進行通知 if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } //...省略非關鍵代碼 observer.mObserver.onChanged((T) mData); }
嚴格的說這裡並不應該叫 LiveData 的激活狀態,而應該是向 LiveData 進行訂閱的 LifecycleOwner 的激活狀態,此時 LifecycleOwner 作為觀察者觀察 LiveData 的變化。所以這裡可能叫 LiveData 在每一個 LifecycleOwner 上的分身的激活狀態更合適,為瞭表述方便,我們就統稱叫 LiveData 的激活狀態。我們將在 2.2 節描述 LifecycleOwner 如何訂閱 LiveData。
以上,隻為瞭說明一個問題:LiveData 需要訂閱 LifecycleOwner,感知其生命周期變化:
圖示說明,LiveData 訂閱 LifecycleOwner,而由 LifecycleOwner.Lifecycle 代理完成生命周期狀態變化通知,所以 LiveData 直接能感知的是 Lifecycle。
2.2 LifecycleOwner 訂閱數據變化
LifecycleOwner 在 STARTED 和 RESUMED 的狀態下可以根據 LiveData 更新 UI 的狀態,所以 LifecycleOwner 需要訂閱 LiveData 的數據變化。
在實際實現當中,LifecycleOwner 作為抽象層並不具體負責訂閱 LiveData,而是由業務層在 LifecycleOwner 中完成具體的訂閱工作,此時我們稱 LifecycleOwner 為 Controller 更合適,雖然它們往往是同一個東西:
註意圖示,一個 User-defined Observer 必須和一個 LifecycleOwner 唯一綁定,否則將無法訂閱。試想,如果一個 Observer 同時綁定兩個 LifecycleOwner:L1 和 L2,假如 L1 處於 RESUMED 的狀態,而 L2 處於 DESTROYED 的狀態,那麼 LiveData 將無所適從:如果遵循 L1 的狀態,將變化通知給 Observer,則更新 L2 會出錯;如果遵循 L2 的狀態,不將變化通知給 Observer,則 L1 得不到及時更新。
2.3 多對多的雙向訂閱網
LiveData 和 LifecycleOwner 之間因為需要相互觀察對方狀態的變化,從而需要實現雙向訂閱;同時,為瞭支持良好的可擴展能力,各自都維護瞭一個觀察者列表,形成一個多對多的雙向訂閱網絡:
我們看到一個 LiveData 是可以同時向多個 LifecycleOwner 發起訂閱的,所以,LiveData 本身其實並不實際維護一個激活狀態,真正的激活狀態維護在 LifecycleOwner 的 User-defined observer 中。
3 LiveData 的事件變化
LiveData 值更新之後的需要通知訂閱者(觀察者),其通知流程非常簡單:
其中,判斷觀察者是否激活,即判斷 LifecycleOwner 是否處於 STARTED 或 RESUMED 狀態,在 2.1 節中已有說明。
我們看一下關鍵的源代碼:
// 入口 @MainThread protected void setValue(T value) { // 必須在主線程調用 assertMainThread("setValue"); //..省略非關鍵代碼 // 設置新值並派發通知 mData = value; dispatchingValue(null); } // 通知派發流程 void dispatchingValue(@Nullable ObserverWrapper initiator) { //..省略非關鍵代碼 // 遍歷觀察者列表 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { // 嘗試通知觀察者 considerNotify(iterator.next().getValue()); //..省略非關鍵代碼 } }
其中 LiveData.considerNotify() 在 2.1 節中已有說明。
4 LifecycleOwner 的事件變化
對於 LifecycleOwner 來說,其變化的事件即為生命周期狀態的變化。在 LifecycleOwner 的事件委托者 Lifecycle 看來,無論是發生瞭 ON_CREATE 事件還是 ON_START 事件,或是任何其它的事件,其事件的切換流程都是通用的。
換言之,隻要 Lifecycle 接口的實現者實現這一通用切換流程,便隻需給 LifecycleOwner 暴露一個切換入口,就能在 LifecycleOwner 的各個生命周期回調函數中調用這個入口就可以瞭。這樣我們在 LifecycleOwner 中應該可以看到形如這樣的流程(偽代碼表示):
public class Activity/Fragment implements LifecycleOwner { @Override public onCrate() { //...省略非關鍵代碼 // 在 Jetpack 框架中,LifecycleImpl 被命名為 LifecycleRegistry LifecycleImpl.handleLifecycleEvent(ON_CREATE); } @Override public onStart() { //...省略非關鍵代碼 LifecycleImpl.handleLifecycleEvent(ON_START); } @Override public onResume() { //...省略非關鍵代碼 LifecycleImpl.handleLifecycleEvent(ON_RESUME); } @Override public onPause() { //...省略非關鍵代碼 LifecycleImpl.handleLifecycleEvent(ON_PAUSE); } @Override public onDestroy() { //...省略非關鍵代碼 LifecycleImpl.handleLifecycleEvent(ON_DESTROY); } }
當然,在具體的源代碼中,與上述偽代碼會有一些出入,但是大體的結構是一致的。在 Jetpack 框架中,這個 Lifecycle 的實現者叫做 LifecycleRegistry。所以我們這裡重點需要關註的就是 LifecycleRegistry 這個 Lifecycle 的代理接口的實現類是如何通知生命周期事件變化的。
4.1 Lifecycle 接口的實現——LifecycleRegistry
4.1.1 LifecycleRegistry 的訂閱實現
如 2.2 節所述,通過 LiveData.observe(owner, user-defined observer),LifecycleOwner 的業務層向 LiveData 訂閱數據變化,而在 LiveData.observe() 方法內,同時會自動通過 Lifecycle.addObserver(LiveData-defined observer) 向 LifecycleOwner 訂閱生命周期變化:
// LiveData.observe() @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { //...省略非關鍵代碼 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //...省略非關鍵代碼 // 向 LifecycleOwner 發起訂閱 owner.getLifecycle().addObserver(wrapper); }
以上方法內的 owner.getLifecycle() 的實際對象即為 LifecycleRegistry,我們來看一下 LifecycleRegistry.addObserver() 的基本訂閱流程:
從整個流程來看,總體可以分為三步:
- 第一步是初始化觀察者對象的狀態,並將觀察者緩存入隊;
- 第二步是以模擬派發生命周期事件的形式,將新加入的觀察者的狀態提升到目前為止可提升的最大狀態;
- 第三步是同步所有觀察者的狀態到全局狀態。
我們可以看到,最後所有的觀察者的狀態都要同步到全局狀態,全局狀態即為 LifecyclerOwner 最新的狀態。那麼為什麼需要進行這麼繁瑣的逐步模擬派發事件來進行同步呢?直接一步到位不行麼?
我們可以考慮一個生命周期感知組件 LifeLocation,其功能是用於進行定位,它訂閱瞭 LifecycleOwner,我們假設 LifeLocation 需要在 CREATED 的時候進行一些必要的初始化,而在 STARTED 的時候開始執行定位操作。假如在 LifecycleRegistry 中的狀態同步可以一步同步到全局狀態,那麼有可能當前的全局狀態已經是 RESUMED 的瞭,這樣 LifeLocation 既得不到初始化,也無從啟用定位功能瞭。
所以,以上這種看似繁瑣的模擬派發狀態事件的步驟是完全必要的,它讓用戶自定義的生命周期感知組件的狀態切換流程是可預測的。
4.1.2 LifecycleRegistry 中的事件流
我們在 4.1.1 節中的流程圖的第 6 步中提到,要根據 observer.state 來計算下一個狀態事件,也就是說按照事件的流向,根據當前的狀態,下一個要發生的事件是什麼。我們修改一下 1.2 節的時序圖如下:
觀察到圖中左邊的藍色箭頭,舉個例子,假如當前的狀態是 CREATED,那麼接下來要發生的事件應該是 ON_START。藍色箭頭指示的事件流方向是生命周期由無到生的過程,我們稱為 upEvent 流;與此對應,右邊的紅色箭頭指示的事件流方向是生命周期由生到死的過程,我們稱之為 downEvent。
4.1.1 節中的流程圖的第 6 步中正好需要進行 upEvent 流操作。除此以外,我們在第 7 步同步到全局狀態時,還需要用到 upEvent 和 downEvent 流操作,且在 LifecycleOwner 的每一次生命周期的變化中,都需要進行上述第 7 步的狀態同步操作。接下來我們就看一看,當 LifecycleOwner 生命周期變化後,發生瞭什麼。
4.1.3 處理生命周期的變化
在 4 節開頭我們描述瞭 LifecycleImpl.handleLifecycleEvent() 方法,在 LifecycleRegistry 中也有一個同名的方法,其功能就是處理 LifecycleOwner 生命周期的變化。handleLifecycleEvent() 的處理過程是這樣的:
如圖所示:
- Sync 標記的部分是進行狀態同步的核心流程,同時也是 4.1.1 節流程圖中的第 7 步的具體實現;
- 每一次生命周期的變化有可能是從無到生的 up 變化,也有可能是從生到死的 down 變化;
- 如果是 up 變化,則需要進行 upEvent 流處理,如果是 down 變化,則需要進行 downEvent 流處理;
- 根據 4.1.1 節的描述,我們可以得出,在觀察者隊列中的所有觀察者,從最老(最開始)到最新(最末),必定維持一個不變性質:非降序排列;
- 所以當 STATE < eldestState 時,說明觀察者隊列中的所有觀察者狀態都大於全局狀態,這時候說明生命周期變化順序是 down 方向的,需要進行 downEvent 流處理;
- 而當 STATE > newestState 時,說明觀察者隊列中的所有觀察者狀態都小於全局狀態,這時候說明生命周期變化順序是 up 方向的,需要進行 upEvent 流處理;
- 無論是 downEvent 流還是 upEvent 流,都會逐步派發生命周期事件給各個觀察者。
關於 downEvent 流和 upEvent 流,我畫瞭一張更加形象的圖用以加深理解:
至此,整個 LiveData 和 Lifecycle 的原理就介紹完成瞭。
5. 關於觀察者模式的一點思考
不難看出,LiveData 和 Lifecycle 的核心是觀察者模式。無論是 LiveData 還是 Lifecycle,它們的共同點就是都需要維護一個穩定的狀態機:
- LiveData 的狀態機就是數據值的變化,每個值就是一個狀態,理論上可以是一個無限狀態機;
- Lifecycle 的狀態機就是生命周期的變化,每個生命周期階段就是一個狀態,它是一個有限狀態機。
在涉及到狀態機模型時,如果我們需要感知狀態機當前的狀態,一般有兩種方式:主動詢問和被動通知。在復雜的業務中,主動詢問狀態機往往是不好的實踐;而被動通知,可以讓我們的業務按照狀態進行清晰的分段,更易於模塊化和測試。觀察者模式就是一種很好的被動通知模式。
所以,當我們的對象維護瞭一個狀態機的時候,可以考慮是否可以采用觀察者模式來讀取狀態。但是需要註意的是,觀察者模式內部是維護瞭一個觀察者引用的列表的,當狀態發生變化的時候,是采用順序遍歷的方式逐個進行通知的,可以想到,當一個被觀察者中維護的觀察者數量很多,其中又有很多觀察者對狀態的響應處理都比較耗時的話,會出現性能瓶頸。尤其是在基於單線程的 UI 環境下,更加需要引起註意,我們通常應該有一個機制來移除不再需要的觀察者,以減輕通知負載。
說明:
該文檔參考的 androidx 版本為
core: 1.1.0
lifecyle: 2.2.0-alpha01
fragment: 1.1.0-alpha09
到此這篇關於Android mvvm之LiveData原理案例詳解的文章就介紹到這瞭,更多相關Android mvvm之LiveData原理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Android開發Jetpack組件Lifecycle原理篇
- Android Jetpack 組件LiveData源碼解析
- Android開發Jetpack組件Lifecycle使用篇
- Android內存泄漏的原因及解決技巧
- Android實現消息總線的幾種方式詳解