我在Kotlin的嵌套片段中遇到了一些问题。我有ViewModel嵌套的片段。从后退按钮恢复片段后,尽管我的数据没有更改,但再次按下viewModel LiveData上的所有观察者触发器。
首先我用谷歌搜索并尝试在字段变量中定义观察者,并检查它是否已初始化,然后不再观察它: lateinit var观察者:观察者
fun method(){
if (::observer.isInitialized) return
observer = Observer{ ... }
viewModel.x_live_data.observe(viewLifecycleOwner ,observer)
}
因此,首先进入片段时它可以正常工作,并且在恢复后它不会在没有数据更改的情况下再次触发,但是在数据更改时也不会触发! 发生了什么事?
答案 0 :(得分:2)
LiveData
始终存储最后一个值,并将其发送给每个已注册的观察者。这样,所有观察者都具有最新状态。
在使用viewLifecycleOwner
时,先前的Observer已被破坏,因此注册新的Observer绝对是正确的事情-您需要新的Observer及其现有状态来填充新的Observer返回片段后创建的(因为将片段放回堆栈时原始视图被破坏了。)
如果您尝试将LiveData用于事件(即,值仅应处理一次),则LiveData并不是最佳的API,因为您必须创建an event wrapper或类似的东西来确保它仅处理一次。
答案 1 :(得分:0)
知道发生了什么之后,我决定使用定制的实时数据来触发一次。 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(...)
}