【AAC 系列三】深入理解架構元件:LiveData

程式亦非猿發表於2019-05-22

0. 前言

本文是深入理解「Android Architecture Components」系列文章第三篇 原始碼基於 android.arch.lifecycle:livedata-core:1.1.1

【AAC 系列一】Android 應用架構新時代來臨!

【AAC 系列二】深入理解架構元件的基石:Lifecycle

【AAC 系列三】深入理解架構元件:LiveData

這個連結不要點,千萬不要

在之前我們深入研究了 Lifecycle 的實現原理,並在文末提到了LiveData 以及 ViewModel,這次我們來講講 LiveData。

LiveData 是 Android Architecture Components 中的一員,先看下官方是如何介紹的:

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state. [見 9.1]

This class is designed to hold individual data fields of ViewModel, but can also be used for sharing data between different modules in your application in a decoupled fashion. [見 9.2]

簡單講 LiveData 是一個能夠感知生命週期、可觀察的資料持有類 ,它被設計成 ViewModel 的一個成員變數;可以以一個 更解耦 的方式來共享資料

實際使用下來發現 LiveData 有幾個特性

  1. LiveData 的實現基於觀察者模式;
  2. LiveData 跟 LifecycleOwner 繫結,能感知生命週期變化,並且只會在 LifecycleOwner 處於 Active 狀態(STARTED/RESUMED)下通知資料改變;
  3. LiveData 會自動在 DESTROYED 的狀態下移除 Observer ,取消訂閱,所以不用擔心記憶體洩露

那麼 LiveData 上述特性的原理是怎麼樣的呢?
使用 LiveData 又需要注意些什麼呢?

本文將圍繞此展開。

1. LiveData 的基本使用

雖然 LiveData 通常跟 ViewModel 配合使用,不過也可以單獨使用,為了簡單起見,這裡不配合 ViewModel。

以下是一個簡單的例子:

MutableLiveData<String> liveString = new MutableLiveData<>();
liveString.observe(this, new Observer<String>() {
  @Override
  public void onChanged(@Nullable final String s) {
    Log.d(TAG, "onChanged() called with: s = [" + s + "]");
  }
});

liveString.postValue("程式亦非猿");
複製程式碼

執行後可以看到日誌輸出:onChanged() called with: s = [程式亦非猿]

釋義:
定義一個 MutableLiveData (LiveData 的一個常用子類),通過 observe 方法可以訂閱修改資料的通知,通過 postValue()  或者 setValue()  方法可以更新資料,已經訂閱的 Observer 能夠得到資料更改的通知,也即回撥 onChanged() 方法。

這樣就算是用上 LiveData 了。

接下來,上乾貨!

2. LiveData 的原理分析

在分析原理前,再明確一下我們的疑問:

  1. LiveData 是如何跟 LifecycleOwner 進行繫結,做到感知生命週期的?
  2. LiveData 只在 LifecycleOwner active 狀態傳送通知,是怎麼處理的?
  3. LiveData 會自動在 DESTROY 的狀態下取消訂閱,是怎麼處理的?
  4. 通過 setValue()/postValue() 更新資料的處理流程是如何?
  5. 生命週期變化後資料處理流程是怎麼樣的?

同時提前看下我整理的 LiveData UML 圖,對 LiveData 有個整體的瞭解,後續的涉及到的類都在這裡了,有助於理解。

image.png

(圖1.LiveData 類圖)

OK, here we go!

2.1 LiveData.observe()

LiveData 的使用流程從 observe() 開始,我們們嘗試從 observe() 方法 開始分析:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        //如果是 DESTROYED 的狀態則忽略
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        //把 Observer 用 LifecycleBoundObserver 包裝起來
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //快取起來
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        //如果已經 observe 過 並且兩次的 owner 不同則報錯
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        //繫結 owner
        owner.getLifecycle().addObserver(wrapper);
    }
複製程式碼

可以看到 observe 方法裡把我們傳遞的 observer 用 LifecycleBoundObserver  包裝了起來,並且存入了 mObservers  ,並且跟 owner 進行了關聯。

並且做了兩個特殊處理:

  1. 忽視處於 DESTROYED 的 owner 的註冊行為
  2. 將一個 Observer 同時繫結兩個 owner 的行為視為非法操作,也即一個 Observer 只能繫結一個 owner,而 owner 可以有多個 Observer;

這裡出現了幾個新的類 LifecycleBoundObserverObserverWrapper 來看看。

2.2 LifecycleBoundObserver

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            // 判斷 owner 當前的狀態是否是至少 STARTED
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            //生命週期改變,如果是 DESTROYED 就自動解除
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //ObserverWrapper.activeStateChanged
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }
複製程式碼

**ObserverWrapper **:

    private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }
				//是否是 active 狀態
        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            //如果 active 狀態下,則傳送資料更新通知
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
複製程式碼

仔細看下這兩個類其實就能解答疑問了。

LifecycleBoundObserver 是 抽象類 ObserverWrapper 的子類,重寫了 shouldBeActive() 方法,在 owner 處於至少是 STARTED 的狀態下認為是 active 狀態;並且它也實現了 GenericLifecycleObserver 介面,可以監聽 lifecycle 回撥,並且在 onStateChanged() 方法裡處理了生命週期改變的事件,當接收到 DESTROYED 的事件會自動解除跟 owner 的繫結,並且將下個流程交給了 activeStateChanged() 。

到這裡 【2.1】、【2.3】的問題已經有了答案:

【2.1】答:LifeData 在 observe 方法中用 LifecycleBoundObserver 包裝了 observer ,並且通過它繫結了owner。
【2.3】答:LifecycleBoundObserver 在 onStateChanged() 方法裡處理了生命週期改變的事件,當接收到 DESTROYED 的事件會自動解除跟 owner 的繫結。

這裡需要注意的是,當我們呼叫 observe() 註冊後,由於繫結了 owner,所以在 active 的情況下,LiveData 如果有資料,則 Observer 會立馬接受到該資料修改的通知。

此時的流程是:

observe-->
  onStateChanged-->
    activeStateChanged-->
     dispatchingValue-->
       considerNotify-->
          onChanged

可以稱之為生命週期改變觸發的流程,另外還有一種流程是 postValue&setValue 觸發的流程,共兩種。

2.3 activeStateChanged(boolean)

在 activeStateChanged() 方法裡,處理了 onActive() 跟 onInactive() 回撥的相關邏輯處理,並且呼叫了dispatchingValue(this) 。(MediatorLiveData 用到了 onActive() 跟 onInactive() 有興趣自行了解,這裡不展開)

接下去探索 dispatchingValue

2.4 dispatchingValue(ObserverWrapper) 分析

    private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        //如果正在分發則直接返回
        if (mDispatchingValue) {
            //標記分發失效
            mDispatchInvalidated = true;
            return;
        }
        //標記分發開始
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            //生命週期改變呼叫的方法 initiator 不為 null
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                //postValue/setValue 方法呼叫 傳遞的 initiator 為 null
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        //標記分發結束
        mDispatchingValue = false;
    }
複製程式碼

considerNotify(ObserverWrapper)  方法:

    private void considerNotify(ObserverWrapper observer) {
        //檢查狀態 確保不會分發給 inactive 的 observer
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //setValue 會增加 version ,初始 version 為-1
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
複製程式碼

可以看到 dispatchingValue 正是分發事件邏輯的處理方法,而 considerNotify 方法則確保了只將最新的資料分發給 active 狀態下的 Observer 。

另外也可以看到 LiveData 引入了版本管理來管理資料 (mData)以確保傳送的資料總是最新的。(具體不多講)

dispatchingValue 這裡分兩種情況:

  1. ObserverWrapper 不為 null
  2. ObserverWrapper 為 null

需要著重講一下。

2.4.1 ObserverWrapper 不為 null 的情況

上面提到過,LifecycleBoundObserver.onStateChanged 方法裡呼叫了 activeStateChanged ,而該方法呼叫dispatchingValue(this);傳入了 this ,也就是 LifecycleBoundObserver ,這時候不為 null 。

也就是說生命週期改變觸發的流程就是這種情況,這種情況下,只會通知跟該 Owner 繫結的 Observer。

2.4.2 ObserverWrapper 為 null 的情況

上面我也提前說了,除了生命週期改變觸發的流程外,還有 postValue&setValue 流程,來看下這倆方法。

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        //呼叫 setValue
        setValue((T) newValue);
    }
};

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

@MainThread
protected void setValue(T value) {
    //必須在主執行緒呼叫 否則會 crash
    assertMainThread("setValue");
    mVersion++;//增加版本號
    mData = value;
    //傳入了 null
    dispatchingValue(null);
}
複製程式碼

LiveData 的 postValue 方法其實就是把操作 post 到主執行緒,最後呼叫的還是 setValue 方法,注意 setValue 必須是在主執行緒呼叫。

並且可以看到** setValue 方法呼叫了 dispatchingValue 方法,並傳入了 null ,這個時候的流程則會通知 active 的mObservers**。

到這裡之前的剩下的所有疑問也都可以解答了。

LiveData 的兩個流程都會走到 dispatchingValue 處理分發通知邏輯,並且在分發通知前會判斷 owner 的狀態,再加上 LiveData 本身內部的版本管理,確保了只會傳送最新的資料給 active 狀態下的 Observer

**注意:**LiveData 對同時多次修改資料做了處理,如果同時多次修改,只會修改為最新的資料。

3. 圖解 LiveData

3.1 LiveData 類圖

再看一遍類圖,回顧一下:

image.png

(圖2.LiveData 類圖)

3.2 LiveData 流程圖

Lifecycle 改變觸發流程:

livedata-lifecycle-changes.png

(圖3.Lifecycle 改變觸發流程圖)

Lifecycle postValue/setValue 觸發流程:

livedata-postvalue.png

(圖4.setValue 改變觸發流程圖)

4. LiveData tips and recipes


LiveData 還有很多其他相關知識,這裡列舉一些,更多實踐可以看一下【7.6】。

4.1 Sticky Event

LiveData 被訂閱時,如果之前已經更改過資料,並且當前 owner 為 active 的狀態,activeStateChanged() 會被呼叫,也即會立馬通知到 Observer ,這樣其實就類似 EventBus 的 sticky event 的功能,需要注意的是,很多時候我們並不需要該功能。具體可以看一下【7.6】的處理。

4.2 AlwaysActiveObserver

預設情況下,LiveData 會跟 LicycleOwner 繫結,只在 active 狀態下更新,如若想要不管在什麼狀態下都能接收到資料的更改通知的話,怎麼辦?這時候需要使用 AlwaysActiveObserver ,改呼叫 observe 方法為呼叫 LiveData.observeForever(Observer) 方法即可。

4.3 MediatorLiveData

LiveData 還有一個子類是 MediatorLiveData,它允許我們合併多個 LiveData,任何一個 LiveData 有更新就會傳送通知。比如我們的資料來源有兩個,一個資料庫一個網路,這時候我們會有兩個 DataSource,也就是兩個 LiveData,這個時候我們可以使用 MediatorLiveData 來 merge 這兩個 LiveData。

4.4 Transformations

Transformations 允許我們把一個 LiveData 進行處理,變化成另外一個 LiveData,目前支援 map 跟 switchMap 兩個方法,跟 RxJava 的操作類似。

比如,用 map 把一個 String 型別的 LiveData 轉換成 Integer 型別:

Transformations.map(liveString, new Function<String, Integer>() {
  @Override
  public Integer apply(final String input) {
    return Integer.valueOf(input);
  }
}).observe(this, new Observer<Integer>() {
  @Override
  public void onChanged(@Nullable final Integer integer) {

  }
});
複製程式碼

4.4 LiveDataBus

EventBus 基於觀察者模式,LiveData 也是,所以 LiveData 可以被用來做成 LiveDataBus,有興趣可以搜尋。

5. 知識點彙總

  1. LiveData 的實現基於觀察者模式(reactive patterns);
  2. LiveData 跟 LifecycleOwner 繫結,能感知生命週期變化,並且只會在 LifecycleOwner 處於 Active 狀態(STARTED/RESUMED)下通知資料改變;如果資料改變發生在非 active 狀態,資料會變化,但是不傳送通知,等 owner 回到 active 的狀態下,再傳送通知;
  3. 如果想要一直收到通知,則需要用 observeForever() 方法;
  4. LiveData 會自動在 DESTROYED 的狀態下移除 Observer ,取消訂閱,所以不用擔心記憶體洩露;
  5. 在 LifecycleOwner 處於 DESTROYED 的狀態下,不能訂閱;
  6. postValue 方法其實最後呼叫了 setValue 只不過把操作放到主執行緒,適合在非同步執行緒裡呼叫,setValue 必須在主執行緒裡呼叫;
  7. 如果同時多次呼叫 postValue 或 setValue 修改資料,只會修改成最新的那個資料,也即只會收到一次通知(set post混合呼叫則不一定);
  8. 如果 LiveData 有資料,並且 owner 在 active 狀態下,那麼在訂閱的時候,會立馬收到一次通知
  9. 一個 Observer 例項,只能繫結一個 LifecycleOwner,而一個 owner 可以繫結多個 Observer 例項;
  10. LiveData 利用版本管理、繫結 Lifecycle 確保了只會傳送最新的資料給 active 狀態下的 Observer

6. 總結

LiveData 基於觀察者模式,並且可以感知生命週期,這使得我們使用 LiveData 既可以享受觀察者模式帶來的隔離資料與 UI 等強大的解耦能力,還可以享受感知生命週期帶來的巨大便利。並且還無需擔心記憶體洩露這個令人頭疼的問題。

我們可以使用 LiveData 非常輕鬆地做到一些非常高效的操作,如僅在 active 的狀態下重新整理 UI,可以避免不必要的資料重新整理。

顯而易見 LiveData 本身的優秀特性有著巨大的價值,利用好絕對是架構設計中的一大利器,另外 LiveData 配合 ViewModel 可以發揮更大的價值,機智的你一定已經知道下一篇文章講什麼了。

7. 參考與推薦

  1. LiveData Overview : developer.android.com/topic/libra…
  2. LiveData doc : developer.android.com/reference/a…
  3. developer.android.com/reference/a…
  4. developer.android.com/reference/a…
  5. github.com/googlesampl…
  6. github.com/googlesampl…

公眾號:程式亦非猿

相關文章