Jetpack系列之 LiveData

700 阅读14分钟

LiveData是什么?

摘抄一段官方对LiveData的介绍。LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData具有生命周期感知能力

意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

本文目标

  • LiveData是什么
  • LiveData有什么作用
  • LiveData使用方式
  • LiveData原理是怎样的

LiveData有何作用

1、关注点分离

LiveData可以更好的配合MVVM架构,可以很容易的实现模型驱动界面渲染,从而使得逻辑层和界面层解耦,

2、不会发生内存泄漏

LiveData中注入了宿主的Lifecycle,可以监测界面的生命周期,可在DESTROYED状态下,反注册观察者

3、不会因 Activity 停止而导致崩溃

LiveData只有在界面可见的状态下,即STARTED或者RESUMED状态下才能收到数据。

4、不再需要手动处理生命周期

LiveData在不可见的状态下,会记录下最新的数据,但不会下发。等到界面可见时候,才会下发最新的数据。因为界面的根本是展示数据,当界面不可见的情况下,界面还有变化就没有意义了

5、适当的配置更改

如果由于配置更改而重新创建了 Activity 或 Fragment,LiveData会重新的注入新的Observer,会导致新的Observer可以收到新的数据。

6、共享资源

LiveData本质是观察者模式,存储了需要下发的数据。如果多处引用相同的LiveData,便可达到共享内存作用。

上述作用来自于官方LiveData讲解,总的来说,LiveData是一个可感知生命周期的组件,通过模型驱动的方式渲染界面。很适合状态数据的发送和接收。

LiveData原理

LiveData的结构就是一个观察者模式,所以使用可分为发送数据和监听数据两部分

一、 发送数据

setValue

protected void setValue(T value) {
    // 确保在主线程中
    assertMainThread("setValue");
    // 版本号+1,用来判断是不是新的数据
    mVersion++;
    mData = value;
    // 分发mData数据,
    dispatchingValue(null);
}

LiveData 判断新旧数据,是通过比较version的方式,每当发送个数据时候,版本都会自增

因为都是在主线程中处理,把要发送的数据放置mData缓存即可

最终的分发都会走到dispatchingValue函数

postValue

同setValue不同,postValue可以在子线程中调用。但最终的分发流程还是会在主线程中处理。

// postValue相对于setValue,是可以在非主线程中调用的。但是会丢失中间的数据
protected void postValue(T value) {
    // postTask表示上一个postValue的数据是否已经被主线程处理了,
    // 如果主线程没处理,直接更新缓存中的mPendingData值即可。mPostValueRunnable中处理的时候,会使用最新的数据
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    // 如果主线程还没处理上一个数据,直接return
    if (!postTask) {
        return;
    }
    // 采用Handler处理线程切换,也保证了数据下发的顺序性
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}


private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        // 在读取mPendingData变量的时候加锁。读取该变量时候不允许再变更该变量
        synchronized (mDataLock) {
            newValue = mPendingData;
            // mPendingData操作完成,设置成空闲状态
            mPendingData = NOT_SET;
        }
        // 最终还会走到setValue
        setValue((T) newValue);
    }
};

相比较setValue,postValue要复杂许多,毕竟要涉及到多线程的处理。

postValue分为三部分

  • postValue方法中,更新内存中的数据,并判断上一个数据有没有开始处理,如果没有处理直接返回。

  • 通过Handler方式,将线程切换至主线程,接下来将在主线程中执行mPostValueRunnable

  • 在主线程执行mPostValueRunnable时,将待发送的数据保存至局部变量中,并将mPendingData数据置位NOT_SET,就表示该数据已经开始处理了。最终还是执行setValue((T) newValue);

如果连续的在子线程中发送数据,mPostValueRunnable没有开始处理上一个数据,具有新的数据到来,就会导致丢失中间的值。

dispatchingValue

所以最终还是回到dispatchingValue函数的执行来。

// 分发数据,当dispatchingValue参数为空时候,为全部观察者分发
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // mDispatchingValue表示已经正在分发中,会将mDispatchInvalidated设置为true,用于拦截继续分发
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        // initiator 不等空,表示对initiator该设备单独分发
        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) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated); // 当mDispatchInvalidated为true,代表有新的数据需要分发,继续循环
    mDispatchingValue = false;
}

当dispatchingValue参数为空时候,为全部观察者分发。当不为空的时候,代表只处理传入的观察者即可。

对于单个的观察者的分发处理,走到了considerNotify函数中

considerNotify

@SuppressWarnings("unchecked")
private void considerNotify(ObserverWrapper observer) {
    // mActive表示观察者是不是活跃的,当这个观察者被移除了或者其他原因导致的不在活跃状态
    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;
    }
    // 通过Version来判断该数据是否对该观察者分发过,分发过则不再处理
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // 更新观察者的Version号
    observer.mLastVersion = mVersion;
    // 给观察者分发mData数据
    observer.mObserver.onChanged((T) mData);
}

这里做了3件事

  • 判断是否是活跃状态,对于不活跃的观察者,是不能都接收到新的数据,只有当回归到活跃状态下才能够接收到新的数据

  • 通过版本号,判段该观察者是否已经下发了最新的数据

  • 更新版本号,并下发数据源

小结: 在数据发送端

setValue必须在子线程中调用,postValue可在子线程中调用

postValue分发数据,可能会导致中间的值被丢弃

二、生命周期切换

这里是以LifecycleBoundObserver观察者举例,当我们以observer方法监听的时候,传入的观察者就会包装成LifecycleBoundObserver对象,该对象监听了宿主LifecycleOwner的生命周期,当生命周期变动,会触发数据的下发处理

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

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

    // 默认是可见状态,即STARTED以上的状态才可能接收到新数据
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    // 状态变更的时候,会回调
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        // 如果宿主状态是DESTROYED,则移除观察者
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        // 当状态发生变更时候,一直执行
        while (prevState != currentState) {
            prevState = currentState;
            // 活跃状态变更
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

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

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);
        }
    }
}

对于LifecycleBoundObserver,如果要能收到新的数据,必须在STARTED或者RESUMED状态下才能。

监听状态变更的方法在onStateChanged方法中,如果当前宿主状态是DESTROYED,则可移除观察者。再循环判断两次状态不一致,则执行activeStateChanged方法。

activeStateChanged方法中,如果两次活跃状态不一致才能处理,如果是活跃状态就执行上文中的dispatchingValue方法,判断observer中数据版本不是最新的,就下发最新的数据。

三、监听数据

监听数据有两种方式,observer和observeForever。分别对应LifecycleBoundObserver、AlwaysActiveObserver这两种观察者。

AlwaysActiveObserver意味着,该观察者只要没有被移除,就一直是活跃状态,也就是当数据变更就一定能收到数据。

这里主要以LifecycleBoundObserver这种带生命感知的观察者做讲解。

// 在给定所有者的生命周期内将给定的观察者添加到观察者列表中。事件在主线程上调度。如果 LiveData 已经有数据集,它将被传递给观察者。
// 只有所有者处于Lifecycle.State.STARTED或Lifecycle.State.RESUMED状态(活动)时,观察者才会收到事件
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    // DESTROYED状态不允许添加观察者
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 包装成LifecycleBoundObserver,当STARTED或RESUMED时,观察者才会收到事件
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 该观察者已经添加过 且 两次添加的Lifecycle不一致,直接抛出异常
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    // 已经添加过,不做处理
    if (existing != null) {
        return;
    }
    // 使用宿主的Lifecycle,添加刚包装好的观察者,之后有状态变更的时候,会走到LifecycleBoundObserver中状态回调
    owner.getLifecycle().addObserver(wrapper);
}

小结:

观察者不能多次注入,更不能同一个观察者绑定的生命宿主不一致,否则会抛出异常

将包装好的观察者,监听宿主的生命周期。当生命周期变更时候,也可能触发数据下发

因为LifecycleBoundObserver观察者是感知生命周期的,就可能出现这样情况。有的观察者处在活跃的状态,能收到新的数据。但有的观察者不处在活跃状态,就会导致不能的观察者收到的数据是不一样的。

从上文的分析中,可知一共有三种方式能触发观察者接收数据:

  • 1、setValue等发射数据
  • 2、增加观察者
observe方式
当添加新的Observer观察者,Observer中的数据版本为-1,如果添加观察之前调用setValue或者postValue设置数据,会将LiveData中最新的数据分发给观察者

observeForever
添加的观察者为AlwaysActiveObserver,不受宿主生命状态影响,如果主动移除该观察者,有新数据变更就能收到
  • 3、当LiveData中的Lifecycle的生命周期发生变化的时候,可能需要分发新的数据。当观察者处在不活跃的状态下,更改了数据,LiveData只会保存,不会分发给observer方式添加的观察者。当宿主生命周期变更时候,会让观察者重新变成活跃状态,从而下发最新的数据,保证观察者中数据最新。

LiveData副作用

粘性事件:之前有通过setValue或者postValue设置过数据,当添加新的Observer会将最新的数据下发至该观察者

数据倒灌:LiveData中的数据有且只能被一个Observer消费,不能被二次的消费。该数据从业务上理解只能被消费一次的。

举个例子:当在Activity中监听了一个LiveData,正常运行后。如果配置横竖屏切换导致Activity重绘,重走生命周期,导致注入一个另一个新的观察者。又接收到了上一次的数据。导致逻辑又走了一次。对于有些业务会出现问题。

通常有下列几种解决方式

1、反射

反射更改Observer中的Version值,使其和LiveData中的版本一致,当添加新的观察者时,不会使其下发。

因为注入观察者时候,会比较observer下发的版本是不是小于缓存的数据版本,而判断有新数据,而下发新数据。

2、SingleLiveEvent

class SingleLiveEvent<T> : MutableLiveData<T?>() {
    private val mPending: AtomicBoolean = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner?, observer: Observer<T>) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }

// Observe the internal MutableLiveData
        super.observe(owner, object : Observer<T>() {
            fun onChanged(@Nullable t: T) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t)
                }
            }
        })
    }

    @MainThread
    override fun setValue(@Nullable t: T?) {
        mPending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }

    companion object {
        private const val TAG = "SingleLiveEvent"
    }
}

重写LiveData方式,通过发射和下发加锁的方式,保证发送的数据,最多只能有一个观察者消费。从而解决数据倒灌问题。

3、Event包装

open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

通过是数据包装,在获取的之后,下次获取的数据为空保证,数据被一个观察者处理。

4、 UnPeekLiveData

KunMinX开源的一个解决此类问题的方法

public class ProtectedUnPeekLiveData<T> extends LiveData<T> {

    protected boolean isAllowNullValue;

    private final HashMap<Integer, Boolean> observers = new HashMap<>();

    public void observeInActivity(@NonNull AppCompatActivity activity, @NonNull Observer<? super T> observer) {
        LifecycleOwner owner = activity;
        Integer storeId = System.identityHashCode(observer);//源码这里是activity.getViewModelStore(),是为了保证同一个ViewModel环境下"唯一可信源"
        observe(storeId, owner, observer);
    }

    private void observe(@NonNull Integer storeId,
                         @NonNull LifecycleOwner owner,
                         @NonNull Observer<? super T> observer) {

        if (observers.get(storeId) == null) {
            observers.put(storeId, true);
        }

        super.observe(owner, t -> {
            if (!observers.get(storeId)) {
                observers.put(storeId, true);
                if (t != null || isAllowNullValue) {
                    observer.onChanged(t);
                }
            }
        });
    }
    
    @Override
    protected void setValue(T value) {
        if (value != null || isAllowNullValue) {
            for (Map.Entry<Integer, Boolean> entry : observers.entrySet()) {
                entry.setValue(false);
            }
            super.setValue(value);
        }
    }

    protected void clear() {
        super.setValue(null);
    }
}

他是通过为每个观察者保存一个对应的标识,在分发的时候过滤掉已经发送数据的观察者。

这里通过System.identityHashCode(observer)来判断两次生成的对象是同一个对象。

LiveData相关类或方法

MediatorLiveData

合并多个LiveData,只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者

例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源: 与存储在数据库中的数据关联的 LiveData 对象。 与从网络访问的数据关联的 LiveData 对象。

public class MediatorLiveData<T> extends MutableLiveData<T> {
    private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();

    /**
     * Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
     * when {@code source} value was changed.
     * <p>
     * {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
     * <p> If the given LiveData is already added as a source but with a different Observer,
     * {@link IllegalArgumentException} will be thrown.
     *
     * @param source    the {@code LiveData} to listen to
     * @param onChanged The observer that will receive the events
     * @param <S>       The type of data hold by {@code source} LiveData
     */
    @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
        // 包装成Source观察者,它是继承自Observer,通过observeForever方式监听LiveData数据
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        // 不允许重复添加
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        // 前提是该LiveData的处在活跃状态
        if (hasActiveObservers()) {
            // 添加对LiveData的观察
            e.plug();
        }
    }

    /**
     * Stops to listen the given {@code LiveData}.
     *
     * @param toRemote {@code LiveData} to stop to listen
     * @param <S>      the type of data hold by {@code source} LiveData
     */
    @MainThread
    public <S> void removeSource(@NonNull LiveData<S> toRemote) {
        Source<?> source = mSources.remove(toRemote);
        if (source != null) {
            source.unplug();
        }
    }

    @CallSuper
    @Override
    protected void onActive() {
        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
            source.getValue().plug();
        }
    }

    @CallSuper
    @Override
    protected void onInactive() {
        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
            source.getValue().unplug();
        }
    }

    private static class Source<V> implements Observer<V> {
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;

        Source(LiveData<V> liveData, final Observer<? super V> observer) {
            mLiveData = liveData;
            mObserver = observer;
        }
        // 监听mLiveData的数据
        void plug() {
            mLiveData.observeForever(this);
        }

        // 反注册mLiveData的监听
        void unplug() {
            mLiveData.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable V v) {
            // mLiveData数据有变更,会回调新注册的观察者
            if (mVersion != mLiveData.getVersion()) {
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            }
        }
    }
}

MediatorLiveData本质还是MutableLiveData,它是通过适配的方式,监听被监听的LiveData数据,再回调给MediatorLiveData注入的观察者

mActiveCount 表示活跃观察者的总和 AlwaysActiveObserver -- 在LiveData观察者队列就是活跃的 LifecycleBoundObserver -- 在LiveData观察者队列中,且当前的宿主是在Started、Resume状态。该观察者才能是活跃的状态

2、map

public inline fun <X, Y> LiveData<X>.map(crossinline transform: (X) -> Y): LiveData<Y> =
    Transformations.map(this) { transform(it) }

    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        // 使用MediatorLiveData观察原始的LiveData,完成数据变更后在发射出去
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

LiveData的数据转变,都是基于MediatorLiveData操作的,通过监听原始的LiveData数据源,经过特殊处理后,返回新的结构

3、switchMap

public inline fun <X, Y> LiveData<X>.switchMap(
    crossinline transform: (X) -> LiveData<Y>
): LiveData<Y> = Transformations.switchMap(this) { transform(it) }


    @NonNull
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {

        // 创建一个MediatorLiveData,负责监听传入的LiveData,并返回给调用者,
        // 从而保证了数据源变化,但引用不变
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        // 监听原始的LiveData
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                // 获取通过transform生成的LiveData
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                // 传入的LiveData无变化
                if (mSource == newLiveData) {
                    return;
                }
                // 反订阅上一次的LiveData
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    // MediatorLiveData订阅生成的LiveData的数据源
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            // 当数据源有变化的时候,最后回调数据
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;

原始LiveData数据变更后,返回的会是一个新的LiveData,之前订阅的LiveData会被反订阅。

switchMap方法中的transform为LiveData,所以可以在switchMap中做些在主线程中不能处理的耗时工作,最终通过LiveData回调至主线程

4、DistinctUntilChanged

public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
    final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
    outputLiveData.addSource(source, new Observer<X>() {

        boolean mFirstTime = true;

        @Override
        public void onChanged(X currentValue) {
            final X previousValue = outputLiveData.getValue();
            // 过滤逻辑,
            // 第一次永远可以分发
            // 和上一次不同,才会调用setValue继续分发
            if (mFirstTime
                    || (previousValue == null && currentValue != null)
                    || (previousValue != null && !previousValue.equals(currentValue))) {
                mFirstTime = false;
                outputLiveData.setValue(currentValue);
            }
        }
    });
    return outputLiveData;
}

为了过滤分发相同的数据,只有和上一次的数据不相等,才不会被过滤