Jetpack之LiveData组件解析

作者: 愿天堂没Android | 来源:发表于2022-02-21 20:55 被阅读0次

    一、为什么使用LiveData

    在Android开发之初,大部分代码都放在一个Activity中,导致Activity臃肿且难以单元测试。后来,诞生了MVCMVPMVVM等开发架构,通过分层抽离Activity中的代码。这种分层架构模式可以将逻辑从View中分离出来,但是无法感知Activity的生命周期,Activity 的生命周期必须通知这些组件。

    LiveData是一种可观察的数据存储器类,具有生命周期感知能力,并可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者,这可以很好的解决上述的相关问题。

    如果观察者的生命周期处于STARTEDRESUMED状态,则LiveData会认为观察者处于活跃状态。LiveData通知观察者时会检查其实时状态,仅会通知处于活跃状态的观察者。一个观察者处于 PAUSEDDESTROYED 状态,它将不会收到通知。一旦观察者重新恢复 RESUMED 状态,它将会重新收到 LiveData 的最新数据。

    LiveData可以注册和实现LifecycleOwner接口的对象配对的观察者,当相应的Lifecycle对象状态变为DESTROYED,便可移除此观察者,这样就可以防止内存泄漏问题(当 ActivityFragment 的生命周期被销毁时,系统会立即退订它们)

    LiveData的特点

    1. 基于观察者模式,是一种持有可被观察数据的类。其需要一个观察者对象,一般是Observer类的具体实现。
    2. 可以感知生命周期,在生命周期活跃时更新组件。其中STARTEDRESUMED就是活跃状态
    3. 可以在生命周期结束时立刻解除对数据的订阅,从而避免内存泄漏等问题
    4. 可以解决Configuration Change问题,配置更改而重新创建Activity或Fragment,其会立即接收最新的可用数据。

    二、基本使用

    依赖引入

    dependencies {
        def lifecycle_version = "xxx"
        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
        // Lifecycles only (without ViewModel or LiveData)
        implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
    
        // Saved state module for ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
    
        // Annotation processor
        kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
        // alternately - if using Java8, use the following instead of lifecycle-compiler
        implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
    }
    

    LiveData对象一般使用步骤:

    1. 创建LiveData实例,指定源数据类型,通常在ViewModel中完成
    2. 创建Observer实例对象,实现onChanged()方法,用于接收源数据变化并刷新UI,通常在界面控制器(ActivityFragment)中创建
    3. LiveData实例使用observer()方法添加观察者,并传入LifecycleOwner
    4. LiveData实例使用setValue()/postValue()更新源数据(子线程要postValue

    2.1 简单使用

    简单演示LiveData的使用

    Activity中创建LiveData对象

    LiveData是一个抽象类,不能直接使用,通常用它的直接子类MutableLiveData来实现

    class MainActivity : AppCompatActivity() {
        companion object {
            private const val TAG = "MainActivity"
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val mutableLiveData = MutableLiveData<String>() //指定源数据类型为String
            mutableLiveData.observe(this) {
                Log.d(TAG,"onChanged:$it" )
                Toast.makeText(this,it,Toast.LENGTH_LONG).show()
            }
            mutableLiveData.postValue("Hello LiveData") //更新源数据
        }
    }
    

    observer方法有两个参数:

    1. 参数一:LifecycleOwner,前述代码为MainActivity本身
    2. 参数二:Observer<T>,前述代码新建了一个Observer<String>,在onChange方法中回调

    大多数情况,LiveDataobserve方法放在onCreate中,如果放在onResume中会出现多次调用的情况。前述情况下,onStart、onResume、onPause为活跃状态,onchanged会立即回调

    LiveData还提供了一个observeForever()方法,与observe()的区别在于当LiveData包装的数据发生变化时,无论页面处于什么状态,observeForever()都能收到通知。使用完成后,要记得调用removeObserver()来停止对LiveData的观察,否则LiveData会一直处于激活状态,Activity永远不会被系统自动回收。

    ViewModel中创建LiveData对象

    实际开发中一般建议LiveData对象是存储在ViewModel对象中,主要原因有:

    1. 避免 ActivityFragment 过于庞大,让其专注于数据显示而不负责存储数据
    2. LiveData实例与特定的ActivityFragment 实例分离开,保证LiveData对象在配置更改后继续存在
    class NameViewModel : ViewModel() {
    
        val currentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }
    }
    

    LiveData对象观察

    建议在组件的onCreate() 方法开始观察LiveData对象,因为

    1. 确保系统不会从 ActivityFragmentonResume() 方法进行冗余调用
    2. 确保 ActivityFragment 变为活跃状态后具有可以立即显示的数据

    通常,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。

    如果观察者从非活跃状态更改为活跃状态时也会收到更新。如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。

    class NameActivity : AppCompatActivity() {
    
        private val model: NameViewModel by viewModels()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            ......
            val nameObserver = Observer<String> { newName ->
                nameTextView.text = newName
            }
            model.currentName.observe(this, nameObserver)
        }
    }
    

    在传递 nameObserver 参数的情况下调用 observe()) 后,系统会立即调用 onChanged(),从而提供 mCurrentName 中存储的最新值。

    如果 LiveData 对象尚未在 mCurrentName 中设置值,则不会调用 onChanged()

    2.2 扩展LiveData

    关于LiveData,可以根据实际需要自定义扩展,复写onActive()onInactive()回调方法即可。

    首先看一下官方提供的一个示例

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager = StockManager(symbol)
    
        private val listener = { price: BigDecimal ->
            value = price   //监听到股价变化,
        }
    
        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }
    
        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    }
    
    • LiveData 对象具有活跃观察者时,会调用onActive()方法
    • LiveData 对象没有任何活跃观察者时,会调用onInactive()方法
    • setValue(T)将更新 LiveData 实例的值,并将更改告知活跃观察者。

    至此,你可以使用StockLiveData

    public class MyFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val myPriceListener: LiveData<BigDecimal> = ...
            myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })
        }
    }
    

    LiveData 对象具有生命周期感知能力,这一事实意味着您可以在多个 ActivityFragmentService 之间共享这些对象。你可以将LiveData类实现为一个单例

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager: StockManager = StockManager(symbol)
    
        private val listener = { price: BigDecimal ->
            value = price
        }
    
        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }
    
        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    
        companion object {
            private lateinit var sInstance: StockLiveData
    
            @MainThread
            fun get(symbol: String): StockLiveData {
                sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
                return sInstance
            }
        }
    }
    
    class MyFragment : Fragment() {
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })
        }
    

    只有当 存在活跃的观察者(LifecycleOwner)时 才会连接到 股价更新服务 监听股价变化

    多个 FragmentActivity 可以观察 MyPriceListener 实例。仅当一个或多项系统服务可见且处于活跃状态时,LiveData 才会连接到该服务。

    2.3 转换LiveData

    如果想要在LiveData对象分发给观察者之前对其中存储的值进行更改,或者您可能需要根据另一个实例的值返回不同的 LiveData 实例。可以使用 Transformations 类,该类包括可应对这些情况的辅助程序方法。

    Transformations.map()
    

    该方法对存储在 LiveData 对象中的值应用函数,并将结果传播到下游。

    val mutableLiveData = MutableLiveData<String>()
    mutableLiveData.observe(this) {
        Log.d(TAG, "onChanged:$it")
        Toast.makeText(this, it, Toast.LENGTH_LONG).show()
    }
    //LiveData返回值实例转换
    val transformedLiveData = Transformations.map(mutableLiveData) {name ->
                                                                    "${name}LiveData is great"
                                                                   }
    transformedLiveData.observe(this){
        Log.d(TAG,"onChange2$it")
    }
    mutableLiveData.postValue("Hello LiveData ")
    
    /**打印结果**/
    onChanged:Hello LiveData
    onChange2Hello LiveData LiveData is great
    
    Transformations.switchMap()
    

    map()类似,对存储在LiveData对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象。

    private lateinit var mutableLiveData1:MutableLiveData<String>
    private lateinit var mutableLiveData2:MutableLiveData<String>
    private lateinit var liveDataSwitch:MutableLiveData<Boolean>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mutableLiveData1 = MutableLiveData()
        mutableLiveData2 = MutableLiveData()
        liveDataSwitch = MutableLiveData()
        val transformedLiveData = Transformations.switchMap(liveDataSwitch,
            Function<Boolean?, LiveData<String>> {
                return@Function if (it) mutableLiveData1 else mutableLiveData2
            })
        transformedLiveData.observe(this){
            Log.d(TAG,"onChanged:$it")
        }
        liveDataSwitch.postValue(false)
        mutableLiveData1.postValue("LiveData init")
        mutableLiveData2.postValue("LiveData developer")
    }
    

    新建一个MutableLiveData<Boolean>来控制切换并赋值给liveDataSWitch。当liveDataSwitch的值为true时返回mutableLiveData1,否则返回mutableLiveData2。通过这种方式,达到切换监听的目的。

    2.4 合并多个LiveData源

    MediatorLiveData继承自mutableLiveData,它可以将多个LiveData数据源集合起来,可以达到一个组件监听多个LiveData数据变化的目的

    private lateinit var mutableLiveData1:MutableLiveData<String>
    private lateinit var mutableLiveData2:MutableLiveData<String>
    private lateinit var liveDataManager:MediatorLiveData<String>
    private lateinit var text:TextView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text = findViewById(R.id.tv_text)
        mutableLiveData1 = MutableLiveData()
        mutableLiveData2 = MutableLiveData()
        liveDataManager = MediatorLiveData()
        liveDataManager.addSource(mutableLiveData1){
            Log.d(TAG,"onChange1:$it")
        }
    
        liveDataManager.addSource(mutableLiveData2){
            Log.d(TAG,"onChange2:$it")
        }
        liveDataManager.observe(this){
            Log.d(TAG,"onChanged:$it")
        }
        mutableLiveData1.postValue("LiveData init")
        mutableLiveData2.postValue("LiveData developer")
    
        text.setOnClickListener(){
            mutableLiveData1.postValue("change LiveData1 data")
        }
    }
    
    /**打印结果**/
    onChange1:LiveData init
    onChange2:LiveData developer
    onChange1:change LiveData1 data
    

    通过MediatorLiveDataaddSource将两个MutableLiveData合并到一起,这样当任何一个MutableLiveData数据发生变化时,MediatorLiveData都可以感知到

    三、原理解析

    这里是基于2.4.0版本进行原理分析。

    在学习LiveData的实现原理前,先了解一下几个核心角色:

    • ObserverWrapper
      Observer的包装类,定义了LiveData的生命周期状态监听方法
    • LifecycleBoundObserver
      继承于ObserverWrapper,并最终实现了GenericLifecycleObserver接口
    • MediatorLiveData
      继承至MutableLiveData,可监听其他容器内容的变化,通过addSource()
    • ......

    看一下LiveData的原理实现机制图,如下图所示

    img img

    LiveData的实现原理,简单总结为

    1. LiveData的实现主要可以分为添加观察者、事件回调和事件更新三部分
    2. 添加观察者:将LifecycleOwnerobserver进行包装成可感知生命周期的包装类,添加到LifecycleObserver中,它将在LifecycleOwner更改状态时得到通知
    3. 事件回调:收到LifecycleOwner状态更改通知时,进行状态判断等处理,然后通知之前添加的观察者对象
    4. 事件更新:更新数据版本号,遍历观察者对象,进行事件分发通知

    LiveData其他补充知识

    1. LiveData的观察者只能与一个LifecycleOwner绑定, 否则会抛出异常。而一个 owner 可以绑定多个 Observer 实例
    2. 使用observeForever()方法,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知,使用完注意手动移除
    3. LiveData利用数据版本管理方法,确保只会发送最新的数据,但是要注意数据倒灌

    3.1 添加观察者

    LiveData通过observer()方法来注册观察者,从该方法进行入手

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        //当前绑定的组件(activity或fragment)状态为为DESTROYED的时候,则会忽视当前的订阅请求
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        //创建生命周期感知的观察者包装类
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //如果指定的键尚未与某个值关联,则将其与给定的值关联
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        //对应观察者只能与一个owner绑定
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                                               + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        //添加一个LifecycleObserver,它将在LifecycleOwner更改状态时得到通知
        owner.getLifecycle().addObserver(wrapper);
    }
    

    LiveDataobserver()的主要内容为:

    1. 首先判断LifecycleOwner是否为DESTROYED状态,如果是就直接忽略,不能添加。
    2. 其次用LifecycleOwnerobserver 组装成LifecycleBoundObserver包装实例wrapper
    3. 使用putIfAbsent()方法observer-wrapper作为key-value添加到观察者列表mObservers中。不能添加具有不同生命周期的相同观察者,否则会抛异常,但是同owner可以add多个Observer
    4. 最后用LifecycleOwnerLifecycle添加observer的封装wrapper

    3.2 事件回调

    LifecycleBoundObserver

    LiveData通过observe()方法添加了LifecycleBoundObserver,看一下内部实现

    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() {
            //至少是STARED状态
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
    
        //关键代码
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                                   @NonNull Lifecycle.Event event) {
            //LifecycleOwner变成DESTROYED状态,则移除观察者
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            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);
        }
    }
    

    LifecycleBoundObserver类主要内容是:

    1. 对原始Observer进行包装,把LifecycleOwnerObserver绑定在一起。当LifecycleOwner处于活跃状态, LifecycleBoundObserver就是活跃的观察者。重写了shouldBeActive()onStateChanged()方法。从Lifecycle中可知,其实现了LifecycleEventObserver接口。
    2. LifecycleOwner生命周期状态变化时检查,如果是DESTROYED状态,则移除观察者。
    3. 如果不是DESTROYED状态,将调用父类ObserverWrapperactiveStateChanged()方法处理 这个生命周期状态变化
    4. shouldBeActive()的值作为参数,至少是STARTED状态为true,即活跃状态为true

    ObserverWrapper

    LifecycleBoundObserver继承了ObserverWrapper,看一下其实现

    private abstract class ObserverWrapper {
        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;     //活跃状态未发生改变,不会处理
            }
            mActive = newActive;
            changeActiveCounter(mActive ? 1 : -1);
            if (mActive) {
                dispatchingValue(this); //观察者变为活跃,进行数据分发
            }
        }
        ......
    }
    
    @MainThread
    void changeActiveCounter(int change) {
        int previousActiveCount = mActiveCount;
        mActiveCount += change;
        if (mChangingActiveState) {
            return;     //如果正在变更 则返回
        }
        mChangingActiveState = true;
        try {
            while (previousActiveCount != mActiveCount) {
                boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
                boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
                previousActiveCount = mActiveCount;
                if (needToCallActive) {
                    onActive(); //活跃的观察者数量 由0变为1。这是个空函数,根据需要重写
                } else if (needToCallInactive) {
                    onInactive();   //活跃的观察者数量 由1变为0。空函数,根据需要重写
                }
            }
        } finally {
            mChangingActiveState = false;
        }
    }
    

    LifecycleOwner状态发生改变时,会调用其activeStateChanged()方法,根据活跃观察者数量的变化,分别调用onActive()onInactive()

    如果状态为Active,会调用dispatchingValue方法,并将自身传进去。

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

    dispatchingValue()主要内容是:

    1. 标记事件分发状态,如果正处于分发状态,表示分发无效直接返回
    2. observerWrapper不为空,就使用considerNotify()通知真正的观察者,observerWrapper为空 则遍历通知所有的观察者。

    看一下considerNotify()方法实现

    private void considerNotify(ObserverWrapper observer) {
        //观察者非活跃,直接return
        if (!observer.mActive) {
            return;
        }
       
        //观察者状态活跃,但是当前变为了不可见状态,再调用activeStateChanged方法,并传入false,其内部会再次判断
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //如果数据版本已经是最新的了,那么直接返回
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        //修改数据版本
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);//回调真正的mObserver的onChanged方法
    }
    

    considerNotify()方法主要内容:

    1. 如果ObserverWrappermActive值不为true,就直接return
    2. 如果当前observer对应组件的状态不是Active,就会再次调用activeStateChanged方法,并传入false,其方法内部会再次判断是否执行onActive方法和onInactive方法回调。
    3. 如果判断条件都满足会调用ObserveronChanged方法,这个方法正是使用LiveDataobserve方法的回调。

    considerNotify()方法中也涉及到了相应的观察者数据版本号的比较问题

    1. 当页面从不可见变为可见,将LiveData中的数据版本号跟对应的观察者中的版本号进行比较,如果大于,则调用onChanged()进行数据的回调。
    2. 如果页面为不可见,那么不会进行数据的回调处理。

    所以LiveData注册观察者后的流程为:

    1. 调用 observe() 注册后,绑定了LifecycleOwner,在active状态下,使用LiveDatasetValue发送数据,则 Observer 会立马接受到该数据修改的通知
    2. 状态变更后的触发流程大致为:observe ——> onStateChanged ——> activeStateChanged ——> dispatchingValue ——> considerNotify ——> onChanged
    3. onChanged方法,交给外部开发者处理接收消息事件的逻辑

    3.3 事件更新

    Activity中,通过postValue或者setValue改变LiveData的数据,在通过Observer,观察LiveData数据的变化。那么LiveData的数据更新是如何通知到Observer的?。

    setValue逻辑

    LiveData 更新数据方式有两个,一个是 setValue(), 另一个是 postValue()postValue() 在内部会抛到主线程去执行更新数据,因此适合在子线程中使用;而 setValue() 则是直接更新数据。

    看一下setValue()方法内部实现

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");   //检查是否在主线程
        mVersion++; //默认值是-1,每次更新数据都会自增
        mData = value;  //更新的数据赋值给mData
        dispatchingValue(null); //调用 dispatchingValue()方法并传入null,将数据分发给所有观察者。dispatchingValue如果传入null则是所有的观察者,如果是具体的ObserverWrapper对象,则通知到具体的Observer。
    }
    
    1. setValue()方法会将数据版本号+1,并进行数据分发
    2. 调用dispatchingValue() 进行事件分发。如果参数为null且为active状态,那么会遍历所有的监听者,逐个通知所有观察者进行了数据的变化

    所以setValue后的触发流程为:setValue ——> dispatchingValue(null) ——> considerNotify——> onChanged

    postValue逻辑

    看一下postValue()实现

    protected void postValue(T value) {
        boolean postTask;   //用于判断是否要更新
        //加锁解决多个子线程同时调用问题
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;   //将数据存入 mPendingData
        }
        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()是将消息通过线程池分发到主线程去处理。

    四、常见问题

    4.1 数据倒灌

    使用LiveData的一个常见问题是数据倒灌,表现为用户在页面没有做任何操作,却进行了页面跳转,数据请求等操作。

    LiveData的设计原则:在页面重建时,LiveData自动推送最后一次数据,不必重新去向后台请求

    对于页面重建,常见的场景有:

    1. 屏幕旋转
    2. 系统语言切换
    3. 内存不足,应用在后台被系统杀死。之后用户再重新进入应用
    4. ......

    这里我们讨论的数据倒灌跟前两种场景相关。

    LiveData实例创建建议在ViewModel中,而资源配置变更并不会导致ViewModel实例销毁。

    通过一个示例演示一下数据倒灌问题

    //MainActivity.kt
    private fun initObserver() {
        mViewModel.testLiveData.observer(this) {
            Log.i("MainActivity", "testLiveData value == $it")
            Thread{
                SystemClock.sleep(3000)
                startActivity<SecondActivity>()
            }.start()
        }
    }
    private fun onClick(){
        mBinding.btnTest.setOnClickListener { mViewModel.testLiveData.value = 3 }
    }
    
    //MainViewModel.kt
    val testLiveData = MutableLiveData<Int>()
    

    上述代码示例,如果用户在MainActivity中点击按钮,会将ViewModel中的testLiveData的值置为3,然后延迟3S后会跳转到下一个页面。如果用户返回当前页面,在当前页面旋转屏幕,发现会自动跳转到下一个页面。这就是数据倒灌导致的。

    前述页面旋转重建了,会自动推送最后一次数据。LiveData的事件回调过程是:observe ——> onStateChanged ——> activeStateChanged ——> dispatchingValue ——> considerNotify ——> onChanged。通过断点调试,可将问题定位在considerNotify

    private void considerNotify(ObserverWrapper observer) {
        //观察者非活跃,直接return
        if (!observer.mActive) {
            return;
        }
    
        //观察者状态活跃,但是当前变为了不可见状态,再调用activeStateChanged方法,并传入false,其内部会再次判断
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //此处判断无效导致数据倒灌
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        //修改数据版本
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);//回调真正的mObserver的onChanged方法
    }
    

    LiveData在前述屏幕旋转中也执行了事件分发,其原因是if (observer.mLastVersion >= mVersion) 没有生效。

    mVersion认识

    // LiveData.java   
    static final int START_VERSION = -1;
    private int mVersion;
    

    mVersionLiveData的成员变量,一个LiveData维护一份实例对象

    public LiveData() {
        mData = NOT_SET;
        mVersion = START_VERSION;
    }
    

    在初始化LiveData()时,mVersion设置为-1,之后每次调用setValue等方法,会执行mVersion++自增方法。

    mLastVersion认识

    mLastVersion默认值也是-1。如果分发事件成功,将当前LiveDatamVersion赋值给mLastVersion

    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        // 第一处
        int mLastVersion = START_VERSION;
    }
    private void considerNotify(ObserverWrapper observer) {
        ...
            // 第二处
            if (observer.mLastVersion >= mVersion) {
                return;
            }
        // 第三处
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
    

    屏幕旋转后,该值重新变为-1,导致了数据倒灌问题发生。

    回看一下LiveDataobserver()方法

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        
        ......
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ......
    }
    
    1. Activity等页面重建后,LiveData调用observe(),方法内会new一个新的LifecycleBoundObserver对象,该对象继承ObserverWrapper
    2. ObserverWrapper类初始化会重新初始化 int mLastVersion = START_VERSION;mLastVersion赋值为-1
    3. 因为observer.mLasterVersion < mVersion ,considerNotify()方法中的判断失效,重新分发事件,导致数据倒灌

    页面重建后,会自动推送最后一次数据。为什么页面重建没有手动调用setValue()等方法,也会触发事件分发considerNotify()方法?

    considerNotify()方法在内部做了多重判断,如果如果当前observer对应组件的状态不是Active,就会再次调用activeStateChanged方法,并传入false,其方法内部会再次判断是否执行onActive方法和onInactive方法回调。

    considerNotify()` ->`dispatchingValue()` -> `activeStateChanged()` -> `onStateChanged()
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
                               @NonNull Lifecycle.Event event) {
        //如果当前Activity的状态是onDestory,移除
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        // 上一次的state跟当前的不同时,执行事件分发
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    

    如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。

    简单小结

    1. 页面异常销毁重建,ViewModel会保存销毁之前的数据,在Activity重建完成后进行数据回复,所以LiveData成员变量中的mVersion会恢复到重建之前的值
    2. 页面重建后会调用LiveDataobserve()方法,方法内部会重新new一个实例,会将mLastVersion恢复到初始值。
    3. 由于LiveData本身的特性,Activity的生命周期由非活跃变成活跃时,LiveData会触发事件分发,导致屏幕旋转或者切换系统语言后出现数据倒灌

    数据倒灌解决方法

    4.2 postValue数据丢失

    postValue数据丢失的典型表现就是使用LiveData时,连续postValue两次,发现第一次的值会丢失。

    viewModel.testLiveData1.postValue("hello click")
    viewModel.testLiveData1.postValue("hello kotlin")
    
    /**实际只会打印第二次postValue的值**/
    testLiveData1 value == hello kotlin
    

    看一下postValue的内部实现

    volatile Object mPendingData = NOT_SET;
    
    /**
    * If you called this method multiple times before a main thread executed a posted task, only
    * the last value would be dispatched.
    */
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;   //暂存数据,后面的数据会覆盖前面的
        }
        if (!postTask) {    //保证只抛一个mPostValueRunnabl
            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方法的注释上也说明了这一情况。

    postValue的主要内容:

    1. 每次调用postVaule,会将新值赋给mPendingData
    2. Runnable中进行值的分发,通过ArchTaskExecutor将任务发布到主线程中

    到这其实连续postValue时值会丢失的原因已经清楚了

    1. 调用postValue时,其实只是将值暂存到mPendingData,然后往主线程抛一个Runnable,通过setValue将暂存的值设置进去,回调观察者
    2. 如果在这个Runnable真正执行前多次postValue,只会改变暂存值mPendingData,通过postTask的检测不会再往主线程抛Runnable

    作者:者文
    链接:https://juejin.cn/post/7065693389554974728
    如有侵权,请联系删除!

    相关文章

      网友评论

        本文标题:Jetpack之LiveData组件解析

        本文链接:https://www.haomeiwen.com/subject/iuoilrtx.html