LiveData基础使用方式+工作原理(上篇)

引入androidx后,ViewModel+LiveData搭配Activity/Fragment渐渐成为大家喜爱(习惯)的UI制作方式。
总体来说,这套模式的容易学习,使用也方便,但如果没有详细了解其背后的工作机制,也很容易错误使用造成bug。
在此将分享相关architecture component背后原理,将实际开发中遇到的问题和错误总结一些good practice points,以供参考。

这篇文章主要探讨LiveData的 工作原理基本使用方式

How it works:

  • LiveData::setValue VS LiveData::postValue
  • LiveData::observe(lifecycleOwner, observer)

Good Practice Points:

  • Activity观察LiveData,在onCreate注册观察,livedata.observe(this, Observer<T>{})
  • Fragment观察LiveData,在onCreateView/onViewCreated/onActivityCreated 注册观察,livedata.observe(viewLifeCycleOwner, Observer<T>{})
  • 不可将LiveData直接当作EventBus
  • (Tip)建立一个MutableLiveData实例时,可以MutableLiveData<T>(),无需要造一个无意义的初始值null/emptylist,避免观察到无意义的值。

LiveData::setValue vs LiveData::postValue

更新一个MutableLiveData实例中值,需要通过setValue或postValue方法,其中setValue只能在主线程调用。为什么有这样的设计呢?

阅读LiveData(MutableLiveData的父类)源码,其内部使用了俩个volatile修饰的成员变量,mData和mPendingData。
mData保存了最终数据。LiveData实例暴露给外部取值的getValue方法,以及其内部推送数据给观察者时,使用的都是mData。mData只能通过setValue方法更新,即只能由main thread写入,加上volatile的特性(直写main memory而非cpu cache),写入的新值将对所有线程可见。所以不会出现mData更新后,还有线程读取到更新前数据的情况。

那主线程外更新数据怎么实现呢?就由postValue利用mPendingData完成。
mPendingData中保存了将要但还未被写入mData中的值。postValue方法中,新的数据先被写入mPendingData,然后post一个runnable task到主线程。task中调用setValue方法将mPendingData中的值写入mData,接着清除mPendingData中的值。在postValue和task中,mPendingData的读写都由synchronized block包裹。即postValue中和task中对mPendingData的操作不会并行,避免了postValue对mPendingData的赋值正好被task中mPendingData清除覆盖的情况。

postValue source code

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

另外,postValue时,如果当前已有task就不会再post新的task。所以task被执行前,无论postValue被调用多少次,只有最后一次postValue中传入的值,会被更新入mData。也就是说,观察者们不会观察到之前多次postValue中的数据。
task source code

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

所以,LiveData不能直接被当作事件线使用。它的事件是会“丢失”的。它更像是Rx中的BehaviorSubject而非Observerable。除了上述原因,观察者从inActive状态切换到active状态时,会“主动”观察当前LiveData中的值,也是LiveData不可作事件线用的另一个原因。

LiveData::observe

在activity/fragment中观察LiveData,我们一般都会创建一个observer实例,调用liveData.observe(lifecycleOwner, observer)方法,在observer的回调方法onChanged中,实现UI的更新,无需再判断当前lifecycle来决定是否要更新。

observe source code

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

liveData的成员变量mObservers保存了所有observer和它们各自的相关的lifecycleowner。
在observe方法中,observer和lifecycleOwner包裹成一个新的lifecycleBoundObserver对象,在检查有效性后,observer和包裹后的lifecycleBoundObserver分作为key、value存入mObservers中。
lifecycleBoundObserver实现了LifecycleEventObserver接口。
执行lifecycle.addObserver(lifecycleBoundObserver)后,即可在lifecycle状态(state)变化时,触发lifecycleBoundObserver.onStateChanged,根据lifecycle当前状态,决定observer是否活跃(active), 从而决定是否要接收livedata中的数据更新。
lifeBoundObserver.shouldBeActive方法,定义了lifeCycle至少处于STARTED以及之后状态,observer才是active的。
状态从inactive变成active时,lifeBoundObserver会调用liveData.dispatchingValue(this)方法,让liveData把mData中的告知自己,从而通知其包含的observer。
例如,一个在background的activity回到foreground时,其相关的observer也从inactive变成active,也就是onStart后,马上会收到当前livedata中的值。

需要知道的是,lifeCycle不仅会在state真正改变时通知lifecycleBoundObserver。调用lifecycle.addObserver(lifecycleBoundObserver)后,它会把达到当前状态前经历的所有LifeCycle.Event都发送给lifecycleBoundObserver。
例如,当前lifeCycle状态为RESUMED, lifecycle.addObserver(lifecycleBoundObserver)后,
lifeBoundObserver.onStateChanged 马上连续收到ON_CREATE,ON_START,ON_RESUME三个事件。可以想象成“补收”之前的事件。在这种情况下,因为onStateChanged在收到ON_CREATE时,lifecycle已经处于RESUME状态,再STARTED之后,所以observer已是活跃的。所以比如在activity onResume后,onPause前,让一个obsever开始观察livedata,那么这个observer的onChange方法会立马被调用(如果mData已经设置过的情况)。

所以,observer并不只是再数据变化时收到“推送”,也可在lifecycle状态变化时收到通知。这也是上面提到,不能直接作为事件线使用的另一个原因。

LiveData.observe调用时机

在activity中,一般在onCreate回调中执行liveData.observe(this, Observer<T> { ... })。lifecycleOwner即activity本身。在activity由于销毁时,比如configuration change,livedata会把observer移除,也就不会有memory leak。
由于livedata的observer本身就会在STARTED之后开始观察,无需把绑定observer放在onStart/onResume中。
此外,虽然observe方法不会重复添加同一个obsever对象,如果在onStart/onResume中使用新建了匿名observer对象的方式绑定,liveData.observe(this, Observer<T> { ... }),仍然会造成错误地重复绑定多个观察者。

在fragment中,需注意lifecycleOwner应该使用viewLifecycleOwner,而不是fragment本身(Android Studio已经会提醒)。fragment的lifecycle比较复杂。有些情况下,onDestroy不会调用到,即到达不了DESTROYED状态,造成observer没有被移除,绑定重复的observer。举个例子,一个非retained fragment实例,在configuration change时,将经历onPause>onStop>onDestroyView>onDetach>onAttach>..onStart>onResume, 其lifecycle不会经历destroyed状态,不管将liveData.observe(this, Observer<T> { ... })方法写在onAttach/onCreate/onCreateView/onViewCreated/onActivityCreated哪个回调中,都不可避免建立了重复的observer。使用viewLifecycleOwner,onDestroyView时,observer就会被移除掉。由于viewLifeCycleOwner必须在onCreateView之后才能获取,onCreateView/onViewCreated/onActivityCreated时调用liveData.observe(viewLifeCycleOwner, Observer<T>{})即可。选三个回调中任意一个差别不大,一般写在onActivityCreated里。


LiveData基础的使用介绍到这里,下一篇预告:LiveData基础使用方式+工作员(下篇) MediatorLiveData & Transforms