JetPack之LiveData

1,019 阅读11分钟

JetPack之二:LiveData

1.什么是LiveData?

LiveData是一种类,持有可被观察的数据。LiveData是一种可感知生命周期的组件,它是基于lifecycle组件的,意味着该组件重视其他app组件的生命周期,如Activity、Fragment、Service。该组件能确保,仅仅在Activity\Fragment\Service等组件都处于活跃的生命周期状态的时候,才去更新app组件。Activity、Fragment不用担心会出现内存泄露,在Activity、Fragment销毁时,LiveData会自动解除其注册关系。

2.为什么要用Livedata?

  1. LiveData能确保UI和数据状态相符

    因为是观察者模式,LiveData会在生命周期状态改变时,通知观察者 可以在观察者对象中进行UI的更新操作

  2. LiveData没有内存泄露

    观察者和Lifecycle对象绑定,能在销毁时自动解除注册

  3. LiveData不会给已经停止的Activity发送事件

    如果观察者处于非活跃状态,LiveData不会再发送任何事件给这些Observer对象

  4. LiveData能确保不再需要手工对生命周期进行处理

    UI组件仅仅需要对相关数据进行观察 LiveData自动处理生命周期状态改变后,需要处理的代码。

  5. LiveData能保证数据最新

    一个非活跃的组件进入到活跃状态后,会立即获取到最新的数据 不用担心数据问题

  6. LiveData在横竖屏切换等Configuration改变时,也能保证获取到最新数据

    例如Acitivty、Fragment因为屏幕选装导致重建, 能立即接收到最新的数据

  7. LiveData能资源共享

    如果将LiveData对象扩充,用单例模式将系统服务进行包裹。这些服务就可以在app中共享。 只需要LiveData和系统服务connect,其他观察者只需要监视LiveData就能获取到这些资源

3.简单使用

首先在你的模块build.gradle里面添加依赖:

 dependencies {
     implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
 }

最简单的一种使用是MutableLivedata:

 class MainActivity : AppCompatActivity() {
 ​
     private val mLivedata = MutableLiveData<String>()//1
     private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(mBinding.root)
         mLivedata.observe(this) {//2
             mBinding.textView.text = it
         }
         var num = 0
         mBinding.button.setOnClickListener {
             mLivedata.value = num++.toString()//3
         }
     }
 }

我这里用了viewbinding代替了findviewbyid。首先在1处定义了mLivedata,类型是MutableLiveData,MutableLiveData是LiveData的子类,暴露出了setValue和postValue两个方法。然后在2处订阅,传入的第一个参数是拥有生命周期的组件,一般是Activity或者Fragment或者是fragment里面的veiw,第二个参数是收到数据之后的回调,这里就是用一个textVeiw将值呈现了出来。然后在3处给一个按钮设置监听,每按一次就发送一个数据。大概的用法还是比较简单的。

再看几个比较进阶的用法:

如果我们想要在LiveData对象分发给观察者之前对其中存储的值进行更改,可以使用Transformations.map()和Transformations.switchMap()

map()是在数据分发之前进行一些处理,而switchMap更多是在多条LiveData下选择一条LiveData:

 class MainActivity : AppCompatActivity() {
 ​
     private val mLivedata = MutableLiveData<Int>()
     private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(mBinding.root)
         mLivedata.observe(this) {
             mBinding.textView.text = it.toString()
         }
         var num = 0
         mBinding.button.setOnClickListener {
             mLivedata.value = num++
         }
         Transformations.map(mLivedata) {//1
             return@map it - 10000
         }.observe(this) {
             Toast.makeText(this, it.toString(), Toast.LENGTH_SHORT).show()
         }
         Transformations.switchMap(mLivedata) {//2
             val mutableLiveData1 = MutableLiveData<String>()
             val mutableLiveData2 = MutableLiveData<String>()
             mutableLiveData1.value = (it + 10).toString()
             mutableLiveData2.value = (it - 10).toString()
             if (it < 10) return@switchMap mutableLiveData1
             else return@switchMap mutableLiveData2
         }.observe(this) {
             mBinding.textView2.text = it
         }
     }
 }

用法很简单,在1处使用Transformations.map对值进行处理,这里就是对原值减10000,然后observer新的LiveData,这里就是弹一个吐司。在2处Transformations.switchMap(mLivedata)对原值进行判断,大于10就返回mutableLiveData1,否则mutableLiveData2,然后对新的LiveDataObserver显示在TextView上面。

这两种用法不是很常用。还有一种用法,当有多条LiveData时,我们需要合并成一条LiveData,这时候可以用MediatorLiveData:

 class MainActivity : AppCompatActivity() {
 ​
     private val mediatorLiveData = MediatorLiveData<Int>()
     private val mutableLiveData1=MutableLiveData<Int>()
     private val mutableLiveData2 = MutableLiveData<Int>()
 ​
 ​
     private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
 ​
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(mBinding.root)
         var num = 0
         mBinding.button.setOnClickListener {
             mutableLiveData1.value = num+20
         }
         mBinding.button2.setOnClickListener {
             mutableLiveData2.value = num-20
         }
         mediatorLiveData.addSource(mutableLiveData1){
             mediatorLiveData.value = it
         }
         mediatorLiveData.addSource(mutableLiveData2){
             mediatorLiveData.value = it
         }
         mediatorLiveData.observe(this){
             mBinding.textView.text = it.toString()
         }
     }
 }

首先我们创建了两条MutableLiveData,然后定义了一个MediatorLiveData,用了两个按钮,分别发送LiveData1和LiveData2的值,再将他们加入到MediatorLiveData,MediatorLiveData本身也是LiveData,再对其Observer这样就达到合并多条LiveData的值。到这里LiveData的一些简单用法就是这些了。那大家有没有疑问,Activity是怎么感知LiveData发送了值的?LiveData为什么不会造成内存泄漏?我们来看看它的源码,把它扒个精光。

4.原理

首先我们看LiveData的源码和看LifeCycle的源码一样,从我们写的代码入手,那就是Observer:

 @MainThread
 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
     assertMainThread("observe");
     if (owner.getLifecycle().getCurrentState() == DESTROYED) {//1
         // ignore
         return;
     }
     LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);//2
     ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);//3
     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);//4
 }

被注解在主线程运行,1处的条件判断说明我们在Destroy去Observer是没用的,2处把我们传进来的owner和observer封装成了LifecycleBoundObserver,它的作用我们等会分析,3处把wrapper添加进了一个map里面,说明我们的liveData是可以有多处Observer的,之后是一些安全判断,最后在4处将我们的wrapper注册到lifeCycle,这不就是我们lifeCycle里面学的嘛,那这个LifecycleBoundObserver肯定实现了LifeCycleObserver我们看一看:

 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() {
         return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
     }
 ​
     @Override
     public void onStateChanged(@NonNull LifecycleOwner source,
             @NonNull Lifecycle.Event event) {
         Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();//1
         if (currentState == DESTROYED) {//2
             removeObserver(mObserver);
             return;
         }
         Lifecycle.State prevState = null;
         while (prevState != currentState) {//3
             prevState = currentState;
             activeStateChanged(shouldBeActive());//4
             currentState = mOwner.getLifecycle().getCurrentState();
         }
     }
 ​
     @Override
     boolean isAttachedTo(LifecycleOwner owner) {
         return mOwner == owner;
     }
 ​
     @Override
     void detachObserver() {
         mOwner.getLifecycle().removeObserver(this);
     }
 }

果然,实现了LifecycleEventObserver来感知我们的activity生命周期,那肯定在生命周期回调的方法onStateChanged里面做了文章,我们看,在1处获取了activtiy当前的生命周期状态,2处,处于destroy的话就移除Observer,这说明了当我 们的activtiy销毁的时候livedata会自动解除监听,所以才不会造成内存泄漏。然后在3处判断如果当前状态和即将发生的状态不一样就执行activeStateChanged(shouldBeActive()),那我们看看shouldBeActive:

 @Override
 boolean shouldBeActive() {
     return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
 }
 public enum State {
         /**
          * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
          * any more events. For instance, for an {@link android.app.Activity}, this state is reached
          * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
          */
         DESTROYED,
 ​
         /**
          * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
          * the state when it is constructed but has not received
          * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
          */
         INITIALIZED,
 ​
         /**
          * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
          * is reached in two cases:
          * <ul>
          *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
          *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
          * </ul>
          */
         CREATED,
 ​
         /**
          * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
          * is reached in two cases:
          * <ul>
          *     <li>after {@link android.app.Activity#onStart() onStart} call;
          *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
          * </ul>
          */
         STARTED,
 ​
         /**
          * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
          * is reached after {@link android.app.Activity#onResume() onResume} is called.
          */
         RESUMED;
 ​
         /**
          * Compares if this State is greater or equal to the given {@code state}.
          *
          * @param state State to compare with
          * @return true if this State is greater or equal to the given {@code state}
          */
         public boolean isAtLeast(@NonNull State state) {
             return compareTo(state) >= 0;
         }
     }

就是将当前状态和START状态相减,大于等于0为true,那也就是START和RSUME这两个状态,LiveData才有效,这也符合用户使用习惯,activtiy也是在START和RSUME呈现页面。

再来看看activeStateChanged():

 void activeStateChanged(boolean newActive) {
     if (newActive == mActive) {
         return;
     }
     // immediately set active state, so we'd never dispatch anything to inactive
     // owner
     mActive = newActive;
     changeActiveCounter(mActive ? 1 : -1);
     if (mActive) {
         dispatchingValue(this);
     }
 }

首先判断mActive是否和newActive相等,mActive初始状态是不活越的,为flase,所以在newActive为false时也就是activty为不活越的状态就会直接返回而不会直接分发值,之后如果是活跃的状态就changeActiveCounter(mActive ? 1 : -1);改变activeCounter的值,这个我们用不到,不用管,主要是dispatchingValue,如果是活跃状态就分发值,并且将当前的this传进去,我们看看:

 void dispatchingValue(@Nullable ObserverWrapper initiator) {
     if (mDispatchingValue) {
         mDispatchInvalidated = true;
         return;
     }
     mDispatchingValue = true;
     do {
         mDispatchInvalidated = false;
         if (initiator != null) {//1
             considerNotify(initiator);
             initiator = null;
         } else {//2
             for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                     mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                 considerNotify(iterator.next().getValue());
                 if (mDispatchInvalidated) {
                     break;
                 }
             }
         }
     } while (mDispatchInvalidated);
     mDispatchingValue = false;
 }

主要操作在下面的do while循环里面,判断我们传进来的ObserverWrapper是否为空,不为空就执行considerNotify(initiator);为空就循环遍历我们一开始observer里面添加的observerWrapper,为什么是for循环?因为我们刚才说了,一个liveData可以被多个生命周期组件观测。所以在这里为null的时候从Observers这个map里面去拿。我们看看considerNotify(initiator);

 private void considerNotify(ObserverWrapper observer) {
     if (!observer.mActive) {//1
         return;
     }
     // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
     //
     // we still first check observer.active to keep it as the entrance for events. So even if
     // the observer moved to an active state, if we've not received that event, we better not
     // notify for a more predictable notification order.
     if (!observer.shouldBeActive()) {//2
         observer.activeStateChanged(false);
         return;
     }
     if (observer.mLastVersion >= mVersion) {//3
         return;
     }
     observer.mLastVersion = mVersion;
     observer.mObserver.onChanged((T) mData);
 }

在1处判断,不活越就直接结束。在2处是一些边界处理,上面的注释也说了当activty生命周期改变时,还未来的及改变mActive的值,此时应该先更改活跃的状态再分发值,这其实不重要我们一般碰不到。然后在3处判断上次的version是否比当前的mVersion大,否则再在下面更新mLastVersion,并且执行onChange,也就是我们再observer方法里面传进来的lamda表达式。一开始mLastVersion和mVersion都是为-1的,所以不会执行下面的回调。到这里Observer方法我们从始至终理解了一边。那一开始我们的onChanged不会回调,那什么时候会呢?答案在setValue里面,我们看一看:

 @MainThread
 protected void setValue(T value) {
     assertMainThread("setValue");
     mVersion++;
     mData = value;
     dispatchingValue(null);
 }

注解说明,setValue是在主线程执行,然后mVersion++;因为一开始mVersion是等于mLastVersion的,现在++了,肯定比mLastVersion大,之后dispatchingValue(null),注意看,这时候传入的值为null,也就是我们刚刚说的dispatchingValue传入值为null的时候会for遍历所有的Oberver然后considerNotify,也就到了:

 private void considerNotify(ObserverWrapper observer) {
     if (!observer.mActive) {
         return;
     }
     // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
     //
     // we still first check observer.active to keep it as the entrance for events. So even if
     // the observer moved to an active state, if we've not received that event, we better not
     // notify for a more predictable notification order.
     if (!observer.shouldBeActive()) {
         observer.activeStateChanged(false);
         return;
     }
     if (observer.mLastVersion >= mVersion) {
         return;
     }
     observer.mLastVersion = mVersion;
     observer.mObserver.onChanged((T) mData);
 }

这时候mVsersion小于mLastVersion,所以就是会执行onChanged,也就是我们传进来的lamda表达式,这就说清楚了为什么每次setValue,activity都能收到值得原因啦。那我们再看看liveData的另外一个方法,postValue:

 protected void postValue(T value) {
     boolean postTask;
     synchronized (mDataLock) {
         postTask = mPendingData == NOT_SET;//1
         mPendingData = value;//2
     }
     if (!postTask) {
         return;
     }
     ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);//3
 }
 private final Runnable mPostValueRunnable = new Runnable() {
         @SuppressWarnings("unchecked")
         @Override
         public void run() {
             Object newValue;
             synchronized (mDataLock) {
                 newValue = mPendingData;
                 mPendingData = NOT_SET;
             }
             setValue((T) newValue);//4
         }
  };

postValue是用来在子线程向liveData发送值的,首先判断mPendingData的值是不是还没设置过,是的话才会分发值,并且将value赋给mPendingData,然后将mPostValueRunnable扔到主线程运行,mPostValueRunnable里面把mPendingData赋值给newValue,再将mPendingData置为未设置,最后执行的就是setValue;其实postToMainThread也就是用Handler发送一个消息到主线程:

 @Override
 public void postToMainThread(Runnable runnable) {
     if (mMainHandler == null) {
         synchronized (mLock) {
             if (mMainHandler == null) {
                 mMainHandler = createAsync(Looper.getMainLooper());
             }
         }
     }
     //noinspection ConstantConditions
     mMainHandler.post(runnable);
 }

那你们有没有发现一个问题,既然setValue是用Handler发一个消息到主线程,而Handler消息是个队列,那假如我同时postValue多次,但由于前面的消息还没处理完,mPostValueRunnable还没有运行,mPendingData还没有被置为NOT_SET,postTask就一直为false,所以后面的postValue直接return了,造成我中间postValue的值直接被覆盖了,从而只收到了最新postValue的值,造成了中间值丢失的问题。解决问题很简单,自己切换到主线程然后用setValue,setValue是不会丢值得。

至此,LiveData的源码分析的差不多了,至于liveData的其他什么方法比如说,observerForever,rmoveobserverForever,都比较简单了,而且也不常用,相信你把这些基础的搞懂,其他的不会太难理解。

LiveData数据倒灌

数据倒灌简而言之就是我们并没有给livedata设置数据而收到了旧的数据。

LiveData的数据倒灌问题。就是当我们用LiveData去发送数据发生事件的时候,你会发现事件会多次发生。我们知道我们的LiveData是在onCreate去Observe的,并且通过源码我们知道,Observer的时候内部会new 一个LifecycleBoundObserver,并且将observe.mLastVersion置为默认值-1,但是此时LiveData的mVersion是没有改变的。而我们的LiveData一般是放在viewmoodel中的,liveData肯定比Activity活的时间长,假如我们在onCreate里去Observe并且给LiveData发送数据,在此过后mVersion是++了的也就是说mVersion至少是大于初始值-1的,假如此时Activtiy重建,重走onCreate方法,此时mVersion是保存在LiveData里面,LiveData是在viewmodel里面,LiveData的实例没有被重建,但是重新走了onCreate,重新Observe了,又重新new了一个LifecycleBoundObserver,也就是mLastVersion为-1,所以此时mLastVersion<mVersion,会回调我们的onChanged()。这就是为什么用LiveData发送事件会被消费多次。而数据多贴一遍我们也不会觉得有问题。但是这其实也不是LiveData的一个bug,liveData设计理念就是专注于给UI层发送数据,对于事件这种我们更应该使用flow或者rxjava。但是把LiveData改改也能用:

 class SingleLiveData<T> : MutableLiveData<T>() {
     private val mPending = AtomicBoolean(false)
 ​
     @MainThread
     override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
         if (hasActiveObservers()) {
             Log.w(
                 "SingleLiveEvent",
                 "Multiple observers registered but only one will be notified of changes."
             )
         }
         super.observe(owner) { t ->
             if (mPending.compareAndSet(true, false)) {
                 observer.onChanged(t)
             }
         }
     }
 ​
     @MainThread
     override fun setValue(t: T?) {
         mPending.set(true)
         super.setValue(t)
     }
 ​
     @MainThread
     fun call() {
         value = null
 ​
     }
 }

AtomicBoolean是通过原子方式更新 boolean 值,能够保证线程安全。这也是官方Demo里面的解决方案,用个boolean记录一次value是否被消费。一次setValue对应一次onChanged并且这个Boolean值是保存在LiveData中的,这样就能用于发送事件了。

其实最佳的解决方案就是不要用LiveData去发送“事件”。

好了以上就是LiveData的一些介绍,喜欢的点点关注点点赞哟。