未调用 Android LiveData 的观察器(但它与 observeForever 一起使用)
Observer for Android LiveData not called (but it is with observeForever)
我在显示项目列表的 ViewPager
上有一个 Fragment
。单击一个,打开一个新的详细信息-Activity
,您可以在其中操作此项目(例如更改名称)。当您 return 到列表时,我希望列表/项目反映更新。
我正在使用 Realm
和 LiveData
的组合(添加了 RealmChangeListener
并更新了实际 LiveData
的 value
)很好。
此外,我正在使用 https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808 中的 "reobserve" 方法 (#3),当重新添加 ViewPager 的片段时它工作正常。
当更改列表片段本身的列表项时,会调用观察器,一切正常。但是当 从不同的 activity (详细信息)进行更改时,不会调用 LiveData-observer。
然而,当使用 observeForever()
而不是 observe(LifecycleOwner)
时,观察者 被调用 即使是从另一个 activity (其中是我想要的)。
片段仅停止(当您从详细信息返回列表时恢复)。它没有分离,没有被破坏,它的视图仍然存在等等。所以 observeForever-observer 可以更新 UI 就好了(即使不可见)。
但我想使用正常 observe()
的 "add and forget" 方法,我不需要移除观察者。
有谁知道为什么不调用观察者?如前所述,片段只是停止了,视图仍然存在(当变化发生在同一个 activity 而不是不同的
时,它被调用。
因为 observeForever()
工作,我只能想到一个错误的生命周期,其中观察者被标记为 "stopped" 而不是 "resumed" 返回时?
编辑 1
根据要求,这是将 RealmResult
作为 LiveData
(来自 blog post at realm)的代码:
Realm.getDefaultInstance()
.where<Model>()
.findAllAsync()
.asLiveData()
fun <T : RealmModel> RealmResults<T>.asLiveData() = RealmResultsRealmData(this)
class RealmResultsRealmData<T : RealmModel>(private val results: RealmResults<T>) : LiveData<RealmResults<T>>() {
private val listener = RealmChangeListener<RealmResults<T>> { realmResults ->
value = if (realmResults.isValid) realmResults else null
}
// region LiveData
override fun onActive() {
results.addChangeListener(listener)
}
override fun onInactive() {
results.removeChangeListener(listener)
}
// endregion
}
这工作得很好。我的 ViewModel
在 onCleared()
中调用 realm.close()
所以这应该没问题。
如前所述,我不认为问题出在领域端,因为它在使用 observeForever()
时有效,但在 LiveData
上使用正常 observe()
时无效。 Realm 仅填充 LiveData
并且已验证有效(使用 observeForever()
)。
我最好的猜测仍然是使用 observe()
时观察者的生命周期。但是我不明白为什么当片段得到 "resumed" 时它没有被调用。 The docs 说 "observers also receive an update when they change from an inactive to an active state".
未定义 "active" 状态是什么,但我猜它至少是 "started" 并且当我从详细信息返回时我的片段进入此状态-activity。所以应该用 LiveData
的最新值调用观察者?相比之下,当使用 observeForever()
时,当我还在详细信息时立即调用观察者-activity(因为片段只是停止但仍然处于活动状态?!)。
似乎addChangeListener()
没有用当前值调用监听器。
realm-examples 中的 LiveRealmResults-class 在构造函数中手动执行此操作(当结果已经加载时,因此仅适用于 synchronous查询)。
if (results.isLoaded) {
// we should not notify observers when results aren't ready yet (async query).
// however, synchronous query should be set explicitly.
value = results
}
对于 async-ones,侦听器只会在查询 returns 时被调用(所以这在大多数情况下都有效,因为 LiveData
是已经 active(onActive()
已从系统调用并添加了侦听器)。
在我的例子中(模型被更改 而 LiveData
是 inactive),监听器是 未调用 当它再次激活时(如前所述,addChangeListener()
不会使用当前值调用侦听器)。
所以我的UI(观察LiveData
)没有收到通知并且仍然显示旧数据。
解决方法
我通过将示例构造函数中的相同行添加到 onActive()
(从示例中复制)来解决这个问题。这样,当 LiveData
激活时,侦听器 被调用 并具有当前值(但在未加载结果时不会在初始调用中调用 - 因此不会重复调用)。
override fun onActive() {
super.onActive()
if (results.isValid) { // invalidated results can no longer be observed.
results.addChangeListener(listener)
if (results.isLoaded) {
value = results
}
}
}
我在显示项目列表的 ViewPager
上有一个 Fragment
。单击一个,打开一个新的详细信息-Activity
,您可以在其中操作此项目(例如更改名称)。当您 return 到列表时,我希望列表/项目反映更新。
我正在使用 Realm
和 LiveData
的组合(添加了 RealmChangeListener
并更新了实际 LiveData
的 value
)很好。
此外,我正在使用 https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808 中的 "reobserve" 方法 (#3),当重新添加 ViewPager 的片段时它工作正常。
当更改列表片段本身的列表项时,会调用观察器,一切正常。但是当 从不同的 activity (详细信息)进行更改时,不会调用 LiveData-observer。
然而,当使用 observeForever()
而不是 observe(LifecycleOwner)
时,观察者 被调用 即使是从另一个 activity (其中是我想要的)。
片段仅停止(当您从详细信息返回列表时恢复)。它没有分离,没有被破坏,它的视图仍然存在等等。所以 observeForever-observer 可以更新 UI 就好了(即使不可见)。
但我想使用正常 observe()
的 "add and forget" 方法,我不需要移除观察者。
有谁知道为什么不调用观察者?如前所述,片段只是停止了,视图仍然存在(当变化发生在同一个 activity 而不是不同的
时,它被调用。因为 observeForever()
工作,我只能想到一个错误的生命周期,其中观察者被标记为 "stopped" 而不是 "resumed" 返回时?
编辑 1
根据要求,这是将 RealmResult
作为 LiveData
(来自 blog post at realm)的代码:
Realm.getDefaultInstance()
.where<Model>()
.findAllAsync()
.asLiveData()
fun <T : RealmModel> RealmResults<T>.asLiveData() = RealmResultsRealmData(this)
class RealmResultsRealmData<T : RealmModel>(private val results: RealmResults<T>) : LiveData<RealmResults<T>>() {
private val listener = RealmChangeListener<RealmResults<T>> { realmResults ->
value = if (realmResults.isValid) realmResults else null
}
// region LiveData
override fun onActive() {
results.addChangeListener(listener)
}
override fun onInactive() {
results.removeChangeListener(listener)
}
// endregion
}
这工作得很好。我的 ViewModel
在 onCleared()
中调用 realm.close()
所以这应该没问题。
如前所述,我不认为问题出在领域端,因为它在使用 observeForever()
时有效,但在 LiveData
上使用正常 observe()
时无效。 Realm 仅填充 LiveData
并且已验证有效(使用 observeForever()
)。
我最好的猜测仍然是使用 observe()
时观察者的生命周期。但是我不明白为什么当片段得到 "resumed" 时它没有被调用。 The docs 说 "observers also receive an update when they change from an inactive to an active state".
未定义 "active" 状态是什么,但我猜它至少是 "started" 并且当我从详细信息返回时我的片段进入此状态-activity。所以应该用 LiveData
的最新值调用观察者?相比之下,当使用 observeForever()
时,当我还在详细信息时立即调用观察者-activity(因为片段只是停止但仍然处于活动状态?!)。
似乎addChangeListener()
没有用当前值调用监听器。
realm-examples 中的 LiveRealmResults-class 在构造函数中手动执行此操作(当结果已经加载时,因此仅适用于 synchronous查询)。
if (results.isLoaded) {
// we should not notify observers when results aren't ready yet (async query).
// however, synchronous query should be set explicitly.
value = results
}
对于 async-ones,侦听器只会在查询 returns 时被调用(所以这在大多数情况下都有效,因为 LiveData
是已经 active(onActive()
已从系统调用并添加了侦听器)。
在我的例子中(模型被更改 而 LiveData
是 inactive),监听器是 未调用 当它再次激活时(如前所述,addChangeListener()
不会使用当前值调用侦听器)。
所以我的UI(观察LiveData
)没有收到通知并且仍然显示旧数据。
解决方法
我通过将示例构造函数中的相同行添加到 onActive()
(从示例中复制)来解决这个问题。这样,当 LiveData
激活时,侦听器 被调用 并具有当前值(但在未加载结果时不会在初始调用中调用 - 因此不会重复调用)。
override fun onActive() {
super.onActive()
if (results.isValid) { // invalidated results can no longer be observed.
results.addChangeListener(listener)
if (results.isLoaded) {
value = results
}
}
}