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

栏目: IT技术 · 发布时间: 4年前

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

引入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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入理解C指针

深入理解C指针

[美] Richard Reese / 陈晓亮 / 人民邮电出版社 / 2014-2 / 45.00

深入理解C指针和内存管理,提升编程效率!这是一本实战型图书,通过它,读者可以掌握指针动态操控内存的机制、对数据结构的增强支持,以及访问硬件等技术。本书详细阐述了如何在数组、字符串、结构体和函数中使用指针,同时演示了相应的内存模型及其对指针使用的影响。 指针为C语言带来了强大的功能和灵活性,却也是C语言中最难啃的一块“骨头”。本书旨在帮读者透彻理解指针,解决这个老大难问题。不论是初学者还是经验......一起来看看 《深入理解C指针》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具