LiveData 原理分析

認(rèn)識 LiveData

LiveData 是一種可觀察的數(shù)據(jù)存儲器類。同時它具備生命周期感知能力,確保只更新處于活躍生命周期狀態(tài)的觀察者組件。

LiveData 具有以下優(yōu)勢:

  • 確保界面符合數(shù)據(jù)狀態(tài):當(dāng)數(shù)據(jù)發(fā)生變化時,就會通知觀察者。觀察者監(jiān)聽到有數(shù)據(jù)變化,就會更新界面。
  • 不會發(fā)生內(nèi)存泄漏,不需要手動解除觀察者:因?yàn)榫邆淞松芷诟兄芰?,在生命周期宿主狀態(tài)變?yōu)?DESTROYED 后會自動移除觀察者。
  • 不會因?yàn)樯芷谒拗鳡顟B(tài)不活躍而導(dǎo)致崩潰:當(dāng)生命周期宿主處于非活躍狀態(tài)時,不會接收到任何 LiveData 事件。
  • 數(shù)據(jù)始終保持最新狀態(tài):當(dāng)數(shù)據(jù)更新時,若生命周期宿主處于非活躍狀態(tài),則不會接收到任何 LiveData 事件,在生命周期宿主變?yōu)榛钴S狀態(tài)時會接收最新數(shù)據(jù)。

使用 LiveData

  1. 添加對應(yīng)的依賴:
livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata", version = "2.8.3" }

如果項(xiàng)目使用了 Compose,記得添加以下依賴,針對 LiveData 提供了很多適用于 Compose 場景下的擴(kuò)展方法:

androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version = "1.7.5" }
  1. 創(chuàng)建 LiveData 實(shí)例,指定源數(shù)據(jù)類型:LiveData 通常配合 ViewModel 使用,存儲在 ViewModel 對象中,通過 setValue()/postValue() 更新數(shù)據(jù)
class LiveDataViewModel : ViewModel() {
    private val _data: MutableLiveData<Int> = MutableLiveData<Int>(0)
    val data: LiveData<Int> get() = _data

    fun updateValue() {
        _data.value = (_data.value ?: 0) + 1
    }
}

LiveData 可設(shè)置初始值,上面的例子中設(shè)置了初始值為0,如果沒有設(shè)置初始值,則初始為 null。

  1. 創(chuàng)建 Observer 實(shí)例:通過 observe 方法,傳入生命周期宿主和 Observer 實(shí)例,監(jiān)聽數(shù)據(jù)變化,如果 LiveData 設(shè)置了初始值,則初始值也會調(diào)用一次 observe 方法
viewModel.data.observe(this) {
}

分析 LiveData 原理

LiveData 如何感知生命周期的變化?

對于 Android 中 Lifecycle 不熟悉的,可以先看這篇文章:Android Jetpact Lifecycle 解析。

LiveData的原理是觀察者模式,通過 LiveData#observe 方法監(jiān)聽 LiveData 數(shù)據(jù)變化,需要傳入兩個參數(shù),第一個參數(shù)為生命周期宿主 LifecycleOwner,第二個參數(shù)為 Observer 觀察者。

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 要求在主線程執(zhí)行
    assertMainThread("observe");
    // 宿主生命周期已處于 DESTROYED 狀態(tài),直接忽略
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 將 LifecycleOwner 和 Observer 包裝成 LifecycleBoundObserver
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 加入觀察者集合中
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 已經(jīng)添加到觀察者集合中且綁定的 LifecycleOwner 不是傳進(jìn)來的 owner
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    // 已經(jīng)添加到觀察者集合中且已經(jīng)注冊owner
    if (existing != null) {
        return;
    }
    // 對 Lifecycle 注冊觀察者
    owner.getLifecycle().addObserver(wrapper);
}

首先要求在主線程執(zhí)行,如果宿主生命周期已處于 DESTROYED 狀態(tài),直接忽略。然后將 LifecycleOwner 和 Observer 包裝成 LifecycleBoundObserver,這是最重要的一步,LifecycleBoundObserver 加入到了觀察者集合 mObservers 中,具備了觀察數(shù)據(jù)更新的能力,又對 LifecycleOwner 的 Lifecycle 注冊觀察,具備了生命周期感知的能力。

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    // 生命周期宿主
    @NonNull
    final LifecycleOwner mOwner;

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

    @Override
    boolean shouldBeActive() {
        // Lifecycle 狀態(tài)大于等于 STARTED 時,認(rèn)為是活躍狀態(tài)
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    // 生命周期事件回調(diào)
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        // DESTROYED 狀態(tài),移除觀察者,避免內(nèi)存泄漏
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        // 循環(huán)更新狀態(tài),保證狀態(tài)是最新的
        while (prevState != currentState) {
            prevState = currentState;
            // 修改活躍狀態(tài),若為活躍狀態(tài)則進(jìn)行數(shù)據(jù)分發(fā)
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}

LifecycleBoundObserver 是繼承 ObserverWrapper 的包裝類,實(shí)現(xiàn)了 LifecycleEventObserver 接口,可以收到 Lifecycle 生命周期事件的回調(diào)。

當(dāng) Lifecycle 生命周期變化時,回調(diào) onStateChanged 方法,獲取到最新的生命周期狀態(tài),若為 DESTROYED 狀態(tài),移除觀察者,避免內(nèi)存泄漏,否則通過循環(huán)更新狀態(tài),保證狀態(tài)是最新的。

當(dāng) Lifecycle 狀態(tài)大于等于 STARTED 時,也就是為 STARTED 和 RESUMED 時,認(rèn)為是活躍狀態(tài),會進(jìn)行數(shù)據(jù)分發(fā)。

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    wrapper.activeStateChanged(true);
}

如果想在數(shù)據(jù)更新的時候讓 Observer 立即得到通知,也就是說忽略生命周期狀態(tài),可以使用 LiveData#observeForever 方法,不需要 LifecycleOwner,同時也就需要開發(fā)者手動 removeObserver。

observeForever 中將 Observer 包裝成 AlwaysActiveObserver,AlwaysActiveObserver 也是 ObserverWrapper 的實(shí)現(xiàn)類,其方法 shouldBeActive 固定返回 true,意味著只要有數(shù)據(jù)變化就會進(jìn)行數(shù)據(jù)分發(fā)。

private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        return true;
    }
}

LiveData 更新數(shù)據(jù)過程

LiveData 更新數(shù)據(jù)有兩種方式:setValue 和 postValue,setValue 只能用于主線程,postValue 可用于子線程。

    @MainThread
    protected void setValue(T value) {
        // 只能在主線程
        assertMainThread("setValue");
        // 更新當(dāng)前 LiveData 數(shù)據(jù)的版本號
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

mVersion 用于標(biāo)記 LiveData 數(shù)據(jù)的版本,初始為-1,每更新一次數(shù)據(jù),mVersion 加 1。

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                    //如果 mDispatchInvalidated 為 true,則中斷繼續(xù)遍歷過程
                    //用新值重新循環(huán)一遍
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

dispatchingValue 方法中,用兩個全局的布爾變量 mDispatchingValue 和 mDispatchInvalidated 實(shí)現(xiàn)了新舊值判斷、舊值舍棄、新值重新全局發(fā)布的邏輯。其中需要注意 mObservers 的遍歷過程,由于每遍歷一個 item 都會檢查一次當(dāng)前的 value 是否已經(jīng)過時,是的話則中斷遍歷,所以會存在僅有部分 Observer 收到了舊值的情況。

    private void considerNotify(ObserverWrapper observer) {
        //如果 observer 處于非活躍狀態(tài),則直接返回
        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;
        }
        //根據(jù) observer 自己的 value 版本號 mLastVersion 來決定是否需要向其進(jìn)行更新
        //為了避免重復(fù)向某個 observer 更新
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

postValue 方法不限定調(diào)用者所在線程,不管是主線程還是子線程都可以調(diào)用,因此是存在多線程競爭的可能性的。

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    
    private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            setValue((T) newValue);
        }
    };

postValue 方法將更新的 value 值保存在變量 mPendingData 上,然后把 Runnable 對象 mPostValueRunnable 拋到主線程,其 run 方法中還是使用的 setValue 方法。由于最終更新值的操作仍然是在主線程,所以在 mPostValueRunnable 被執(zhí)行前,所有通過 postValue 方法更新的 value 值都會被保存在變量 mPendingData 上,且只會保留最后一個,直到 mPostValueRunnable 被執(zhí)行后 mPendingData 才會被重置,所以使用 postValue 方法在多線程同時調(diào)用或者單線程連續(xù)調(diào)用的情況下是存在丟值(外部的 Observer 只能接收到最新值)的可能性的。

Livedata 是否具有粘性,及原因

所謂粘性就是新的觀察者被老的數(shù)據(jù)值通知的現(xiàn)象,Livedata 是具有粘性的。

public abstract class LiveData<T> {
    // 存儲數(shù)據(jù)的字段
    private volatile Object mData;
    // 數(shù)據(jù)的版本號
    private int mVersion;
}

LiveData 的數(shù)據(jù)被存儲在內(nèi)部的 mData 變量中,直到有更新的數(shù)據(jù)覆蓋,所以數(shù)據(jù)是持久的。LiveData 使用變量 mVersion 來標(biāo)識數(shù)據(jù)的版本,用于判斷當(dāng)前數(shù)據(jù)是否是最新的。

    @MainThread
    protected void setValue(T value) {
        // 只能在主線程
        assertMainThread("setValue");
        // 更新當(dāng)前 LiveData 數(shù)據(jù)的版本號
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

LiveData 有兩種場景會進(jìn)行數(shù)據(jù)的分發(fā):一種是通過 setValue 方法更新 LiveData 數(shù)據(jù),postValue 方法最終也是通過 setValue 方法更新 LiveData 數(shù)據(jù)的,在 setValue 方法里通過 dispatchingValue 方法進(jìn)行數(shù)據(jù)的分發(fā),會遍歷所有觀察者并進(jìn)行分發(fā);另一種是新增觀察者或者觀察者的生命周期發(fā)生變化(至少為 STARTED),在觀察者 ObserverWrapper#activeStateChanged 方法里也是通過dispatchingValue 方法進(jìn)行數(shù)據(jù)的分發(fā),此時只會給單個觀察者進(jìn)行數(shù)據(jù)分發(fā)。

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

        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }

        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;
            changeActiveCounter(mActive ? 1 : -1);
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
    private void considerNotify(ObserverWrapper observer) {
        //如果 observer 處于非活躍狀態(tài),則直接返回
        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;
        }
        //根據(jù) observer 自己的 value 版本號 mLastVersion 來決定是否需要向其進(jìn)行更新
        //為了避免重復(fù)向某個 observer 更新
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

considerNotify 方法負(fù)責(zé)將數(shù)據(jù)分發(fā)給觀察者,會進(jìn)行三次判斷,首先判斷數(shù)據(jù)觀察者是否活躍,然后判斷數(shù)據(jù)觀察者綁定的生命周期宿主是否活躍(因?yàn)榭赡苌芷谒拗鞯幕钴S狀態(tài)還沒有同步到觀察者的活躍狀態(tài)),最后判斷數(shù)據(jù)觀察者的數(shù)據(jù)版本號是否是最新的,經(jīng)過這三次判斷之后,會將最新的數(shù)據(jù)分發(fā)給觀察者。

因?yàn)樾掠^察者的數(shù)據(jù)版本號初始值為-1,必然小于 LiveData 的數(shù)據(jù)版本號,新觀察者被添加時會觸發(fā)一次數(shù)據(jù)數(shù)據(jù)分發(fā),必然會接收到老的數(shù)據(jù),所以 LiveData 是具有粘性的。

LiveData 的粘性問題,及解決方法

LiveData 的粘性可能會導(dǎo)致一些重復(fù)觸發(fā)的問題,比如常見的橫豎屏切換所導(dǎo)致的 Activity 的重建,Activity 內(nèi)的觀察者也會被重新創(chuàng)建且添加,就會再次收到 LiveData 的數(shù)據(jù),一些業(yè)務(wù)可能在此場景下會導(dǎo)致重復(fù)觸發(fā)的問題,有以下一些解決方法:

  • 引入中間層記錄消費(fèi):在值的外面包裝一層,新增一個標(biāo)記位標(biāo)記是否被消費(fèi)過,這樣就完全去除了 LiveData 的粘性特性。
// 一次性值
open class OneShotValue<out T>(private val value: T) {
    // 值是否被消費(fèi)
    private var handled = false
    // 獲取值,如果值未被處理則返回,否則返回空
    fun getValue(): T? {
        return if (handled) {
            null
        } else {
            handled = true
            value
        }
    }
    // 獲取上次被處理的值
    fun peekValue(): T = value
}
  • 帶有最新版本號的觀察者:LiveData 數(shù)據(jù)分發(fā)給觀察者會進(jìn)行三次判斷,其中一次就是數(shù)據(jù)版本號的判斷,如果觀察者在被創(chuàng)建添加時其數(shù)據(jù)版本號不是初始值-1,而是最新的數(shù)據(jù)版本號,則不會接收到老的數(shù)據(jù)。從外部修改觀察者的數(shù)據(jù)版本號需要通過反射修改。
  • 使用 Kotlin Flow:SharedFlow 的構(gòu)造參數(shù)中有一個參數(shù) replay,表示重新發(fā)射給新的訂閱者的數(shù)據(jù)的數(shù)量,可以將舊的數(shù)據(jù)回播給新的訂閱者,默認(rèn)為0,也就是說不會保留最后發(fā)送的數(shù)據(jù),只會發(fā)送新狀態(tài)更新,設(shè)置為1,效果同 StateFlow 一樣,會保留最后發(fā)送的數(shù)據(jù),也和 LiveData 一樣,變得具有粘性。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容