LiveData和Fragment的奇妙"BUG"

"Bug"再现

在Activity中保存一个ViewModel,使用Navigation组件和Fragment实现页面的切换,AFragment获取Activity的ViewModel并注册LiveData数据为观察者,此时使用setValue让AFragment收到一次LiveData数据,然后切换到BFragment(AFragment销毁),之后切回AFragment,会发现重新注册LiveData数据,AFragment再次收到LiveData数据。

Navigation和LiveData.png

原因分析

LiveData 官网介绍

官方介绍LiveData会一直向活跃的应用组件观察者发送数据,而使用Naviagtion组件时,博主实现的方案导致了每次切换页面都会重走一次Fragment的生命周期,也就是处于“STARTED 或 RESUMED 状态”,导致了从其他页面切换回来之后,会触发LiveData的数据回调。
这里其实是博主对于ViewModel的生命周期理解不够透彻,在AFragment中调用Activity中的ViewModel,导致ViewModel中的LiveData会一直给Fragment发送数据。

解决方案

  1. 合理管理ViewModel的范围,虽然ViewModel可以用来Fragment之间的数据共享,但如果业务范围只跟某个Fragment有关,那么最好就只给这个Fragment使用。这样Fragment在销毁或者创建的时候,也会销毁ViewModel与创建ViewModel,ViewModel携带的LiveData就是全新的不会在发送之前设置的数据。
  2. 使用一个google大神实现的一个复写类 SingleLiveEvent,其中的机制是用一个原子 AtomicBoolean记录一次setValue。在发送一次后在将AtomicBoolean设置为false,阻止后续前台重新触发时的数据发送。
package jp.wasabeef.util

import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.Observer
import android.support.annotation.MainThread
import android.support.annotation.Nullable
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean

/**
 * A lifecycle-aware observable that sends only new updates after subscription, used for events like
 * navigation and Snackbar messages.
 * 

* This avoids a common problem with events: on configuration change (like rotation) an update * can be emitted if the observer is active. This LiveData only calls the observable if there's an * explicit call to setValue() or call(). *

* Note that only one observer is going to be notified of changes. */ class SingleLiveEvent : MutableLiveData() { private val mPending = AtomicBoolean(false) @MainThread override fun observe(owner: LifecycleOwner, observer: Observer) { if (hasActiveObservers()) { Timber.w("Multiple observers registered but only one will be notified of changes.") } // Observe the internal MutableLiveData super.observe(owner, Observer { t -> if (mPending.compareAndSet(true, false)) { observer.onChanged(t) } }) } @MainThread override fun setValue(@Nullable t: T?) { mPending.set(true) super.setValue(t) } /** * Used for cases where T is Void, to make calls cleaner. */ @MainThread fun call() { value = null } }

你可能感兴趣的:(LiveData和Fragment的奇妙"BUG")