尽管数据未更改,但一些片段观察器在从返回堆栈弹出后触发

Some fragment observers trigger after pop from back stack although data is not changed

我在 Kotlin 的嵌套片段中遇到了一些问题。我在 ViewModel 中嵌套了片段。从后退按钮恢复片段后,再次按下 viewModel 上的所有观察者 LiveData 触发器,尽管我的数据没有改变。

首先我在谷歌上搜索并尝试在 filed 变量中定义观察者并检查它是否已初始化然后不再观察它: lateinit var observer: 观察者

fun method(){
        if (::observer.isInitialized) return
        observer = Observer{ ... }
        viewModel.x_live_data.observe(viewLifecycleOwner ,observer)
}

所以一开始进入片段它工作正常而且在恢复之后它不会在没有数据更改的情况下再次触发但它也不会在数据更改时触发! 怎么回事?

LiveData 总是存储最后一个值并将其发送给每个已注册的观察者。这样所有的观察者都有最新的状态。

当您使用 viewLifecycleOwner 时,您之前的 Observer 已被销毁,因此注册一个新的 Observer 绝对是正确的做法 - 您需要新的 Observer 及其现有状态来填充新视图返回 Fragment 后创建的视图(因为当 Fragment 放入返回堆栈时原始视图被销毁)。

如果您尝试将 LiveData 用于事件(即只应处理一次的值),LiveData 不是最好的 API,因为您必须创建 an event wrapper 或类似的东西以确保它只被处理一次。

在知道发生了什么之后,我决定使用自定义的实时数据来触发一次。 ConsumableLiveData。所以我把答案放在这里可能对其他人有帮助。

class ConsumableLiveData<T>(var consume: Boolean = false) : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(
            owner,
            Observer<T> {
                if (consume) {
                    if (pending.compareAndSet(true, false)) observer.onChanged(it)
                } else {
                    observer.onChanged(it)
                }
            }
        )
    }

    override fun setValue(value: T) {
        pending.set(true)
        super.setValue(value)
    }
}

使用方法如下。它只会在任何更新值之后触发一次。这将非常适合处理导航或收听点击或来自用户的任何交互。因为只触发一次!

//In viewModel
val goToCreditCardLiveData = ConsumableLiveData<Boolean>(true)

在片段中:

viewModel.goToCreditCardLiveData.observe(viewLifecycleOwner) {
        findNavController().navigate(...)

    }

如果你正在使用 kotlin 并且只触发一次 data/event 使用 MutableSharedFlow

示例:

private val data = MutableSharedFlow<String>() // init

data.emit("hello world) // set value

lifecycleScope.launchWhenStarted {
      data.collectLatest { } // value only collect once unless a new trigger come
}

MutableSharedFlow 不会触发方向更改或返回上一个片段等