ViewModel 在导航导航中没有被清除,并且 viewmodel 中的实时数据保持活动状态

Posted

技术标签:

【中文标题】ViewModel 在导航导航中没有被清除,并且 viewmodel 中的实时数据保持活动状态【英文标题】:ViewModel not getting cleared in Navigation navigate and live data in viewmodel stays alive 【发布时间】:2020-04-09 21:42:44 【问题描述】:

所以,我已经使用导航实现了具有多个片段模式的单个活动。我将每个片段的视图模型用于非 UI 操作。

问题是当您使用findNavController().navigate() 导航时,片段实际上并没有被销毁。仅调用 onDestroyView。因此,片段的 onDestroy 永远不会被调用,随后视图模型也不会被清除,因此 LiveData 观察者也保持活动状态,当我回到片段时,再次创建观察者,因此观察到两次实时数据。一次是它持有的旧数据,第二次是来自某些操作的新数据。

例如, 我有片段A和片段B

A 显示一个列表,B 您可以添加将显示在列表中的内容。可能会从片段 B 中的 api 获取新数据以显示在 A 中。

所以,当我从片段 B 回到 A 时,观察者会先用旧数据调用两次,然后再用更新的数据调用。最后,列表显示了正确的数据,但我不希望有两个观察者发生。

我关注了这篇文章 https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808

并尝试使用viewLifeCycleOwner 而不是this,但这无济于事,问题仍然存在。

我也尝试在观察之前移除观察者:

vm.ld.removeObservers(this)
vm.ld.observe(viewLifeCyclerOwner, observer)

问题依然存在。

(我也尝试删除onDestroyView 中的观察者,但问题仍然存在。)

我发现的唯一解决方法是在onDestroyView 中手动调用视图模型onCleared 并清除实时数据。

在片段 onDestroyView

vm.clear()

在视图模型中

fun clear() = onCleared()

override fun onCleared() 
  //do stuff

现在,这解决了我的问题。但我觉得这不是一个可靠的解决方案,可以有更好的方法来做到这一点。如果有人能阐明这一点,我会很高兴。谢谢。

【问题讨论】:

兄弟你有解决办法吗?因为我有同样的问题 你可以试试我上面用过的解决方案。但请注意,这不是最佳解决方案。 【参考方案1】:

我浪费了几天时间来解决一个类似的问题。仔细检查您的 VM 是如何初始化的:

val myVm: MyViewModel by activityViewModels()

对比

val myVm: MyViewModel by viewModels()

如果您使用by activityViewModels() 委托,您将指示 android 将 VM 的生命周期与主机活动而非当前片段绑定。因此,即使片段被销毁,您的 VM 也不会被清除。我经过惨痛的教训才学到这个。切换回 by viewModels() 委托会使 VM 的范围仅限于 Fragment 的生命周期。当 Fragment 被销毁时,如果它是唯一的观察者,VM 将清除。

在观察时,我完全被 thisviewLifecycleOwner 的目标搞糊涂了。显然,目标的选择只与您是否打算手动控制显示 DialogFragment 的对话框有关。另一个混乱的宝石。

在您的情况下,如果您在片段之间切换并且 onDestroy 没有被调用,也可能是因为片段被保留。例如,ViewPager2 具有 offscreenPageLimit,它指示 Android 在您切换页面时将隐藏的片段保留在内存中,这进一步增加了必须完全了解所有内容才能使用 SDK 的混乱情况。

【讨论】:

你实际上挽救了我的职业生涯:p【参考方案2】:

您可以在片段中使用 viewModelStore.clear(),然后在 ViewModel 中覆盖 onCleared() 以处理您的需要

【讨论】:

【参考方案3】:

您可以在onDestroyView 中将livedata 的值设置为null。

示例:

override fun onDestroyView() 
   super.onDestroyView()
   vm.ld.value = null

in ViewModel

fun resetLiveData() 
  _ld.value = null


in Fragment

override fun onDestroyView() 
   super.onDestroyView()
   vm.resetLiveData()

【讨论】:

以上是关于ViewModel 在导航导航中没有被清除,并且 viewmodel 中的实时数据保持活动状态的主要内容,如果未能解决你的问题,请参考以下文章

手动清除 Android ViewModel?

清除 sharedViewModel

从选项卡转到根页面

导航返回时不清除文本输入并验证错误消息

如何清除导航堆栈?

从 ViewModel 弹出导航视图