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 的官方文檔中,給出瞭一個非常清晰的時序圖來說明這個模型:

lifecycle_states

  • 生命周期的狀態總共有 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_observe_lifecycleowner

圖示說明,LiveData 訂閱 LifecycleOwner,而由 LifecycleOwner.Lifecycle 代理完成生命周期狀態變化通知,所以 LiveData 直接能感知的是 Lifecycle。

2.2 LifecycleOwner 訂閱數據變化

LifecycleOwner 在 STARTED 和 RESUMED 的狀態下可以根據 LiveData 更新 UI 的狀態,所以 LifecycleOwner 需要訂閱 LiveData 的數據變化。

在實際實現當中,LifecycleOwner 作為抽象層並不具體負責訂閱 LiveData,而是由業務層在 LifecycleOwner 中完成具體的訂閱工作,此時我們稱 LifecycleOwner 為 Controller 更合適,雖然它們往往是同一個東西:

lifecycleowner_observe_livedata

註意圖示,一個 User-defined Observer 必須和一個 LifecycleOwner 唯一綁定,否則將無法訂閱。試想,如果一個 Observer 同時綁定兩個 LifecycleOwner:L1 和 L2,假如 L1 處於 RESUMED 的狀態,而 L2 處於 DESTROYED 的狀態,那麼 LiveData 將無所適從:如果遵循 L1 的狀態,將變化通知給 Observer,則更新 L2 會出錯;如果遵循 L2 的狀態,不將變化通知給 Observer,則 L1 得不到及時更新。

2.3 多對多的雙向訂閱網

LiveData 和 LifecycleOwner 之間因為需要相互觀察對方狀態的變化,從而需要實現雙向訂閱;同時,為瞭支持良好的可擴展能力,各自都維護瞭一個觀察者列表,形成一個多對多的雙向訂閱網絡:

bidirection_subscribes

我們看到一個 LiveData 是可以同時向多個 LifecycleOwner 發起訂閱的,所以,LiveData 本身其實並不實際維護一個激活狀態,真正的激活狀態維護在 LifecycleOwner 的 User-defined observer 中。

3 LiveData 的事件變化

LiveData 值更新之後的需要通知訂閱者(觀察者),其通知流程非常簡單:

livedata_setvalue

其中,判斷觀察者是否激活,即判斷 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() 的基本訂閱流程:

lifecycleregistry_addobserver.png

從整個流程來看,總體可以分為三步:

  • 第一步是初始化觀察者對象的狀態,並將觀察者緩存入隊;
  • 第二步是以模擬派發生命周期事件的形式,將新加入的觀察者的狀態提升到目前為止可提升的最大狀態;
  • 第三步是同步所有觀察者的狀態到全局狀態。

我們可以看到,最後所有的觀察者的狀態都要同步到全局狀態,全局狀態即為 LifecyclerOwner 最新的狀態。那麼為什麼需要進行這麼繁瑣的逐步模擬派發事件來進行同步呢?直接一步到位不行麼?

我們可以考慮一個生命周期感知組件 LifeLocation,其功能是用於進行定位,它訂閱瞭 LifecycleOwner,我們假設 LifeLocation 需要在 CREATED 的時候進行一些必要的初始化,而在 STARTED 的時候開始執行定位操作。假如在 LifecycleRegistry 中的狀態同步可以一步同步到全局狀態,那麼有可能當前的全局狀態已經是 RESUMED 的瞭,這樣 LifeLocation 既得不到初始化,也無從啟用定位功能瞭。

所以,以上這種看似繁瑣的模擬派發狀態事件的步驟是完全必要的,它讓用戶自定義的生命周期感知組件的狀態切換流程是可預測的。

4.1.2 LifecycleRegistry 中的事件流

我們在 4.1.1 節中的流程圖的第 6 步中提到,要根據 observer.state 來計算下一個狀態事件,也就是說按照事件的流向,根據當前的狀態,下一個要發生的事件是什麼。我們修改一下 1.2 節的時序圖如下:

lifecycleregistry_event_flow

觀察到圖中左邊的藍色箭頭,舉個例子,假如當前的狀態是 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() 的處理過程是這樣的:

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 流,我畫瞭一張更加形象的圖用以加深理解:

downevent_and_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!

推薦閱讀: