Android Jetpack--LiveData篇

LiveData

可观察的、具有生命周期感知的数据存储类

  • 当生命周期处于active 状态时,LiveData存储的数据发生变更时能够通知数据的观察者
  • 因为对生命周期具有感知能力,在生命周期处于销毁状态时会主动移除注册的观察者,保证其能正常被销毁,防止内存泄露

下面就看下LiveData如何让进行的数据存储,怎样做到生命周期的感知,以及如何让数据变更通知到观察者的

数据保存

LiveData更新数据通过setValue和postValue完成

注:LiveData是抽象类,且setValue和postValue都是protected,直接使用LiveData时不能进行数据更新的,可以使用可修改的LiveData–MutableLiveData

简单看下setValue和postValue是怎么进行的数据保存

setValue
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");//检查是否在主线程调用,未在主线程调用会抛出异常
        mVersion++; //数据版本,每次数据更新版本都会递增,具体的用途后面介绍
        mData = value;  //当前保存的数据,通过getValue也能看出,获取当前保存的数据直接返回的就是mData
        dispatchingValue(null); //这就是被观察数据的分发,如何将当前的数据分发给观察者
    }
postValue

setValue是在主线程调用然后直接将数据发送给了主线程中的观察者,而postValue则是用来将子线程的数据发送到主线程的观察者的

    /**可以看下源码里作者对postValue方法的注释说明
     * Posts a task to a main thread to set the given value. So if you have a following code
     * executed in the main thread:
     * If you called this method multiple times before a main thread executed a posted task, only
     * the last value would be dispatched.
     *
     * 1、发送一个任务到主线程去设置更新数据,即无论是主线程还是子线程调研postValue都能将数据发送到主线程更新并通知
     * 2、在主线程执行更新数据之前多次调用该方法多次更新数据的话,只有最后一次的值能够分发出去,中间的值将不会被发送到观察者,
     *    即用postValue连续发送1、2、3三个数,主线程的观察者可能只能收到最后一个值3的通知
     * 为什么会出现使用postValue发送数据中间数据观察者会收不到通知这种情况呢?
     * 下面我们就带着这个问题去阅读下postValue的实现,看完也就明白为什么了
     **/
     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和PostValueRunnable,观察两函数内有一个核心的成员变量mPendingData

mPendingData根据语义和代码分析可知,该变量的值表示的是PostValueRunnable等待更新保存的数据,默认静态常量NOT_SET标识无需要保存的数据

  1. 所以postValue内先根据mPendingData == NOT_SET这个条件判断了是否有需要更新保存的数据,有需要保存的数据才会调用后续的通过PostValueRunnable将任务切换到主线程进行数据的保存

  2. mPostValueRunnable内先将待保存的数据mPendingData值赋给newValue,然后将mPendingData重置为不需要保存的默认值,接着通过setValue(newValue)直接保存了数据

上面提出的问题是什么原因造成的

  • 我们接着看步骤1,当mPendingData == NOT_SET条件不成立时,表示有正在等待保存的数据,此时mPendingData会被更新为postValue新传入的值,

  • 如果此时上一个mPendingData的值还未通过mPostValueRunnable内的setValue将数据更新保存,则当执行到PostValueRunnable内更新保存数据时保存的数据已经变更成了新的待保存的值

  • 进而中间等待保存的值就在保存的过程中丢失了,导致observer观察到数据变更的值是最新的数据,中间的数据未被更新保存进而也不会通知到观察者

所以,如果在子线程使用postValue更新数据,数据频繁更新的情况下就会出现无法将所有值的更新都通知到观察者。

感知生命周期

LiveData添加数据的观察通过方法observe添加观察者
observe需要两个参数:LifecycleOwner和 Observer,前者和生命周期有关,后者则是数据的观察者
那么分析LiveData如何对生命周期感知的,那肯定和LifecycleOwner有关,先来看下LifecycleOwner
LifecycleOwner接口只有一个接口函数Lifecycle getLifecycle(),而Lifecycle就是生命周期的感知类,通过Lifecycle能获取到其当前的生命周期状态,还可以注册监听生命周期状态发生变化,关于详细Lifecycle详细的可以去看Jetpack下的lifecycle库
我们来看observe方法的实现

    @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);
        //mObservers保存了所有的数据观察者,同一个观察者不能重复注册
        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;
        }
        //这里lifecycle添加了生命周期状态变化的注册
        owner.getLifecycle().addObserver(wrapper);
    }

主要看下LifecycleBoundObserver和ObserverWrapper

LifecycleBoundObserver实现了LifecycleEventObserver接口,并且继承于ObserverWrapper,完成了Lifecycle的生命周期监听,主要看下当生命周期变化时都做了什么?

    @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;
        //判断有生命周期变化(与当前保存的生命周期不一致)
        //保存当前的生命周期状态,调用父类ObserverWrapper的activeStateChanged
        //主要是更新父类的Active状态
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

ObserverWrapper是观察者Observer的扩展包装类,主要是保存了Observer、当前的生命周期状态是否是active、当前数据的版本,关键点也是当生命周期发生变化时对应的处理。

    //更新mActive的状态值,如果从非活跃状态变更为活跃状态,还需要发送通知,通知观察者当前保存的最新的数据
    //当然,已经通知过的数据不会再通知更新,只会通知在非活跃状态时新保存的数据,当变更为活跃状态才会更新
    //比如:如果使用observer注册观察数据,如果时activity,当activity处于后台时观察者并不会收到数据的更新
    //当activity回调前台时才会将后台时发生变化的数据最新一条数据通知到观察者
    //如果activity后台也需要收到数据变化的通知,使用observeForever注册观察者
    //可以发现observeForever只有观察者一个参数,并无生命周期相关内容
    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        mActive = newActive;
        changeActiveCounter(mActive ? 1 : -1);
        if (mActive) {
            dispatchingValue(this);
        }
    }
数据更新通知

主要是dispatchingValue方法

简单看下,主要就是通知分发过程的一些状态管理和调用observer的回调通知数据的更新,

当传入observer时,直接通知该observer更新,无传入observer时,通知的是全部observer数据更新

    @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) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

具体通知observer的方法是considerNotify

可以看到,只有当observer包装类的active为活跃状态时才会继续调用的observer的回调方法;

并且还做了数据的version判断,在上面刚开始看到的setValue方法里面可以看到,每次数据更新其version都会自增,此处判断已经发送的version数据不会重复发送,所以如果一个activity或者fragemnt处于后台时没有数据的更新,当切换到前台时触发通知当前最新的数据也不会执行,应为该数据更新已经通知过了。

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

MediatorLiveData

简介与原理分析

–LiveData的一个扩展实现

–可以用来观察另外一个LiveData数据的变化

–For example:有LiveData A、LiveData B,当A发生变化的时候,B的数据需要根据A的值发生变化,这时候就可以使用MediatorLiveData B来监听LIveData A的变化;

如下例子实现了当filter变化时,filterResult根据filter的变化及时变更状态

private val _filter = MutableLiveData<String>()
    val filter: LiveData<String> = _filter

    private val _filterResult = MediatorLiveData<String>()
    val filterResult: LiveData<String> = _filterResult

    init {
        _filterResult.addSource(_filter) {
            _filterResult.value = "filterResult on filter changed: $it"
        }
    }

    fun setFilter(filter: String) {
        _filter.value = filter
    }

简单分析下其原理:

    /**
     * MediatorLiveData添加观察的数据源和监听回调的方法是addSource,那我们就从该方法入手
     * addSource传入两个参数
     * @params source 观察变化的数据源
     * @params onChanged 被观察的数据源源发生变化时的回调
     **/
    @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {

        //1、创建了一个Source对象,构造函数包含了观察变化的数据源和数据变化的回调监听
        //构造的Source关联了被观察对象和观察者,完成了观察者的注册绑定
        Source<S> e = new Source<>(source, onChanged);
        //2、这里做了检查,防止同一个数据源被同一个MediatorLiveData多次注册观察
        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;
        } 
        /**
          * 3、private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
          * mSources是MediatorLiveData里面所有需要监听的数据集
          * 当MediatorLiveData收到状态Active的回调时,会遍历所有source,调用source的plug方法,开始绑定需要观察的数据源的变化监听
          * 这里时判断当MediatorLiveData已经处于Active状态了,新添加的source就要立刻进行绑定监听
          * 如果MediatorLiveData未处于Active状态,新添加的source需要观察的数据会被添加到mSources中,在收到Active状态变化时统一进行观察者与被观察者的关联绑定
         **/
        if (hasActiveObservers()) {
            e.plug();
        }
    }

    //下面时Source类源码
    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;
    }

    //该方法就是上面绑定Source里面关联的被观察数据与观察对象的方法,可以看到实际上就是调用的LiveData的observe方法
    void plug() {
        mLiveData.observeForever(this);
    }

    void unplug() {
        mLiveData.removeObserver(this);
    }

    //被观察的数据发生变化时调用观察数据变化的回调通知数据更新
    @Override
    public void onChanged(@Nullable V v) {
        if (mVersion != mLiveData.getVersion()) {
            mVersion = mLiveData.getVersion();
            mObserver.onChanged(v);
        }
    }
}

应用

androidx.lifecycle包下的Transformations类为我们提供了几个LiveData的扩展函数,其中map和switchMap就充分的利用了MutableLiveData可以观察其他LiveData数据源的这一特性。

map
    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

map函数,其实就是创建了一个MutableLiveData,然后调用addSource添加了对数据变化的监听,当数据发生变化时,再调用传入的回调方法Function<X, Y> mapFunction去处理当被观察的数据发生变化时其所需要做的数据转换,然后将转换后的值更新到MutableLiveData,这种方式主要是用于map产生的MutableLiveData对象保持不变的情况,即map只产生一个MutableLiveData对象,每次被观察的数据变化后经过mapFunction函数回调转化后的新数据都是更新到同一个MutableLiveData上。熟悉RxJava的可以看出,该函数和RxJava中的map操作符基本一致,可以用来做数据的转换。

switchMap
    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

switchMap与map均可以用来做数据的转换,但是也存在着一定的区别,可以对比着来看:

switchMap的switchMapFunc函数返回的不是直接返回转换后的数据,而是返回一个新的MutableLiveData,如果switchMapFunc返回的MutableLiveData与当前MutableLiveData是同一个对象则不进行处理,如果返回的不是同一个对象移除对旧livedata的监听,将监听对象变为新的livedata。

合并多个LiveData源

利用MutableLiveData可以观察其他LiveData数据源的特性,可以用来合并多个LiveData源,能够实现只要任何一个被观察的LiveData源发生了变化,MutableLiveData的观察者都能收到数据更改的通知。

    private val localData = MutableLiveData<String>()
    private val remoteData = MutableLiveData<String>()
    private val data = MediatorLiveData<String>()

    init {
        data.addSource(localData) {
            data.value = "data from local: $it"
        }
        data.addSource(remoteData) {
            data.value = it
        }

    }

如上例子,假如View层需要更新某个数据的变化,这个数据可以分别来自本地和远端网络,则可以通过上述方式,MediatorLiveData data分别添加对本地数据和远端网络数据源变化的观察,当观察到本地数据和远端网络数据源变化时去更新MediatorLiveData的数据,View层只需要观察MediatorLiveData的变化就能同时监听到来自本地数据和远端网络数据的变化

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值