JetPack–LiveData使用及原理解析

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

一、概述

LiveData作为2017Google I/O大会作为架构组件的一部分被共同发布出来。LiveData是具有生命周期感知,可观察数据的容器。能够确保只有在组件处于活跃状态时才会更新数据。它不仅可以和ViewModel组合使用,同时也可以单独使用。LiveData基于观察者模式设计,并且只会更新处于活跃状态的观察者(START 及RESUME),而处于非活跃状态的观察者不会收到通知。

二、简介和基础使用

2.1LiveData的简介

使用 LiveData 具有以下优势:

确保界面符合数据状态

LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。

不会发生内存泄漏

观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

不会因 Activity 停止而导致崩溃

如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

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

界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

数据始终保持最新状态

如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

适当的配置更改

如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

共享资源

您可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData

2.2LiveData用法

2.2.1 基本用法
  //MainActivity.kt
  mainViewModel.navigateToDetailAction.observe(viewLifecycleOwner, EventObserver { //1
             pokemon ->
                //跳转到另一个页面
                findNavController().navigate(toPokemonDetail(pokemon),
                    FragmentNavigatorExtras(cardView to pokemon.url))

        })
.....省略.....
//MainViewModel.kt
   private val navigateToDetailAction = MutableLiveData<Pokemon>()
   fun openDetail(pokemon: Pokemon) {
        navigateToDetailAction.value = pokemon //2
    }
复制代码

注释1处的observe方法有两个参数分别是LifecycleOwner和 Observer ,第一个参数就是MainActivity本身,第二个参数新建了一个Observer,在onChanged方法中得到回调。注释处的postValue方法会在主线程中更新数据,这样就会得到打印的结果。

三、原理

3.1 LiveData类图

LiveData类图

可以看到LiveData类十分简单。
首先LiveData本身是一个抽象类,但是其并没有抽象方法。它有个LifecycleBoundObserver内部类,实现了LifecyclerEventObserver接口,这样当生命周期有变化时,就会通过onStateChanged方法回调告知。并继承ObserverWrapper抽象类,用于处理观察者的配置。
MutableLiveData为LiveData的实现类,其内部只是将setValue及postValue方法设置为public.用于说明其内部值是可变的。
MediatorLiveData可以看成是多个LiveData的代理,当将多个LiveData添加到MediatorLiveData,任何一个LiveData数据发生变化时,MediatorLiveData都会收到通知。

3.2源码解析

在源码解析前,将带着以下三个问题进行源码的阅读
1.LiveData是如何知道生命周期变化的?
2.LiveData调用observe方法后是如何回调通知的?
3.LiveData的postValue/setValue方法的流程?

我们把最常用到的observe方法作为源码解析的入口

   @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //1.只能在主线程添加监听
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
       //2.生成了绑定了Lifecycle生命周期的Observer
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
       //3.将观察者添加到Map中。putIfAbsent方法为如果当前key已经存在,则返回对应的value。
        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;
        }
        //4.使得LiveData获得生命周期感知的能力
        owner.getLifecycle().addObserver(wrapper);
    }
    
  }
复制代码

在调用LiveData.observer()方法时,传递的第一个为 LifecycleOwner,第二个参数Obserser实际就是我们的观察后的回调。

第二步将两个参数传入了包装类,在包装类中实现了Lifecycle生命周期的监听及obersrver的操作。
第三步将观察者及上面的包装类存储到mObservers中。如果在Map中已经存在则返回对应的值。
第四步:添加到Lifecycle的观察者队列中。使其能够感知到生命周期变化。

接着我们来分析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() {
            //此代表了在Acitivty中,处于onStart,onResume,onPause生命周期时,LiveData才会处于活跃状态。
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
         @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            //1.在Lifecycle处于销毁状态时,移除了观察者。这样就阻止了内存泄漏的发生
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //2.更新LiveData的活跃状态
            activeStateChanged(shouldBeActive());
        }
        ....省略代码
        LiveData#ObserverWrapper.class
        void activeStateChanged(boolean newActive) {
            //如果状态没有变化,则直接返回
            if (newActive == mActive) {
                return;
            }
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            //处于活跃状态的observer个数从0到1
            if (wasInactive && mActive) {
                //默认空实现
                onActive();
            }
            //处于活跃状态的observer个数从1到0
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                //默认空实现
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
复制代码

dispatchingValue的具体实现

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        //正在处于分发状态
        if (mDispatchingValue) {
            //分发无效
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
             //在setValue方法中会调用此方法,此时initiator为null,则会通知所有处于活跃状态的观察者。否则只会通知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);
        //标记分发结束
        mDispatchingValue = false;
    }


    @SuppressWarnings("unchecked")
    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        //首先判断是否处于活跃状态,避免在非活跃状态时收到回调
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //version判断。如果观察者的version大于当前version则不通知更新.
        //因此LiveData并不会根据具体值是否改变而去判断是否需要通知观察者
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        // 调用onChanged方法告知
        observer.mObserver.onChanged((T) mData);
    }
复制代码

LiveData的postValue/setValue方法分析
首先,修改LiveData的值有两个方法,一个是setValue(),一个是postValue()。从名字上就可以知道两个方法的区别。postValue方法是可以在子线程中调用的,最终通过Handler调用setValue方法。因此下面直接分析setValue方法的流程

  private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            setValue((T) newValue);
        }
    };
...省略
 protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        //  最终会在主线程调用setValue方法
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

  @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        //增加version的值
        mVersion++;
        mData = value;
        //在setValue调用完之后,通知其它的观察者更新
        dispatchingValue(null);
    }
复制代码

四、常见疑问

1.在Activity的任何生命周期下修改Livedata值,都会收到回调吗?
答:不会,使用observer方法监听时,只有处于STARTED状态的Lifecycle才能收到回调

2.什么情况下LiveData处于Active状态?
答:是由ObserverWrapper(私有类)中的shouldBeActive方法的实现决定。在使用observe()方法监听时,默认实现类为LifecycleBoundObserver

@Override
boolean shouldBeActive() {
   return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
复制代码

3.LiveData为什么不会内存泄漏?
答:如前面分析所述

4.LiveData特性导致的问题。–网上常说的黏性事件
答:

   //MainViewModel.kt
   private val navigateToDetailAction = MutableLiveData<Pokemon>()
   fun openDetail(pokemon: Pokemon) {
        navigateToDetailAction.value = pokemon
    }
  //MainActivity.kt
  mainViewModel.navigateToDetailAction.observe(viewLifecycleOwner, Observer {
             pokemon ->
                //跳转到另一个页面
                findNavController().navigate(toPokemonDetail(pokemon),
                    FragmentNavigatorExtras(cardView to pokemon.url))

        })
复制代码

调用了openDetail方法后,改变了LiveData中的值,就会通知到MainActivity中的观察者。然后拉起了一个界面,此时如果点返回键,会发现马上又跳回该页面,造成无法返回的现象。
这是由于当前界面跳转到另一个页面时,active由活跃变为了不活跃,而返回到该页面时,由不活跃变为了活跃状态,(触发了LifecycleBoundObserver 的onStateChanged()方法)因此此时回调会被调用,从而再次跳转到了第二个页面。
解决方法:1.在此种场景下,让LiveData只调用一次。调用一次后不再通知。

5.如果想忽略生命周期变化一直监听要如何实现?
答:使用observeForever添加监听,使用此方法添加监听后,注意:在销毁时需要手动手动调用removeObserver()移除监听,否则可能会造成内存泄漏

6.使用相同的值去调用setValue及postValue方法时,是否会收到回调?
答:会,详细看上面的源码分析。与version相关,跟具体值无关。

7.为什么LiveData会带有粘性特性?
       假设在activity1中,有定义LiveData变量a,此时你并没有在该页面添加观察者,而是在activity2的onCreate中添加观察者。
当你在activity1,setValue或者postValue后,跳转到activity2,你会发现activity2中的观察者的回调起效了。仔细看源码,你会发现因为当你打开activity2,并且添加了观察者时,符合了生命周期变动 并且由于 observer.mLastVersion < mVersion ,因此调用了observer的onChanged方法。

8.为什么会在添加监听后会收到多次回调?
答:排查思路:
1.查看对应LiveData 监听者的数量。是否在无意中多次调用了observe方法。比如:在onResume中调用observe方法。
2.查看在添加监听前,是否使用了postValue更新数据。

五、总结

本篇主要介绍了LiveData的用法及原理分析。总的来说,LiveData是基于Lifecycle实现了数据分发能力。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享