为啥用 viewLifecycleOwner 观察到的 LiveData 在 onDestroyView 之后会得到回调?

Posted

技术标签:

【中文标题】为啥用 viewLifecycleOwner 观察到的 LiveData 在 onDestroyView 之后会得到回调?【英文标题】:Why would a LiveData observed with viewLifecycleOwner get callbacks after onDestroyView?为什么用 viewLifecycleOwner 观察到的 LiveData 在 onDestroyView 之后会得到回调? 【发布时间】:2021-12-17 23:45:50 【问题描述】:

我有一个使用 View Binding 的 Fragment,因此它将其 _binding 成员变量设置为 onCreateView 并在 onDestroyView 中将其清空。在onViewCreated 中,我使用viewLifecycleOwner 作为LifecycleOwner 从视图模型观察LiveData。 (实际上这是在片段和基类之间划分的,但我看不出这将如何解释)

我无法在内部重现这一点,但 crashlytics 报告了在绑定为 null 时 LiveData 的观察者被回调的字段中的案例,这让我认为它是在调用 onDestroyView 之后以某种方式被调用的。知道这怎么可能吗?

更新:原来观察者在其中一个视图上调用了 postDelayed,因此在调用 onDestroyView 之后正在执行 Runnable(并访问绑定)。

感谢@Zain 和@EpicPandaForce 观看。

【问题讨论】:

可能是因为MyFragmentBase被多个分片扩展;所以每当onDestroyView()MyFragment 调用;你得到了同一个基类的另一个片段;那么这并不意味着观察者是分离的;它仍在工作,因为它与 ViewModel 的生命周期相关联;不是片段。 observe(viewLifecycleOwner) 不是表示观察者与片段的生命周期相关联吗? 我需要查看 NPE 的确切异常堆栈跟踪来告诉您出了什么问题。我感觉这是一个进程死亡+片段重新创建的问题,与 LiveData 无关。 如果这真的是 LiveData 中的错误,我正在考虑跳转到 Flow + flowWithLifecycle/repeatOnLifecycle,但如果确实是进程死亡,那么也许这也无济于事。跨度> 【参考方案1】:

observe(viewLifecycleOwner) 不是意味着观察者与片段的生命周期相关联吗?

是的,但这并不意味着观察者已经死了,LiveData 本身在ViewModel 中维护,并且观察者对象仍然存在于片段的旧实例中

因为这个LiveData 跨越了MyFragmentBase 片段;在片段之间切换最终应该将一个全新的观察者附加到 LiveData,但旧的观察者并没有从 liveData 分离。

因此,您可以尝试使用removeObserver() 销毁片段时分离/移除观察者:

abstract class MyFragmentBase : Fragment() 
    ...

    protected abstract val someView: View
    
    val observer = Observer<Boolean>  flag ->
        someView.isVisible = flag
       
 
    override fun onViewCreated(
        view: View, 
        savedInstanceState: Bundle?
    ) 
        super.onViewCreated(view, savedInstanceState)
        
        // Attach the observer
        viewModel.someLiveData.observe(viewLifecycleOwner, observer)       
       
    


    override fun onDestroyView() 
        super.onDestroyView()
        // Detach the observer
        viewModel.someLivedata.removeObserver(observer)
    

    ...

这也应该消除附加到被破坏片段的实时观察者的任何内存泄漏

【讨论】:

但是片段的viewLifecycleOwner 的全部意义不是只有在onCreateViewonDestroyView 之间才会回调观察者吗?如果片段不在它们之间,如何调用旧的观察者? (这是 _binding 为空的唯一情况) 例如 LiveData.observe 的 cmets 状态“如果所有者移动到 DESTROYED 状态,观察者将自动被移除。” android.googlesource.com/platform/frameworks/support/+/… 有道理;但由于某种原因,这对你不起作用 ViewLifecycleOwner 将删除onDestroyView 中的观察者,不需要此代码 @EpicPandaForce 非常感谢;我确实相信这一点,而且LiveData 具有生命周期意识;但认为这需要明确删除.. 由于某种原因,肯定不会调用 onDestroyView

以上是关于为啥用 viewLifecycleOwner 观察到的 LiveData 在 onDestroyView 之后会得到回调?的主要内容,如果未能解决你的问题,请参考以下文章

使用 viewLifecycleOwner 作为 LifecycleOwner

使用 viewLifecycleOwner 的生命周期范围从 Fragment 启动协程

为啥用示波器观察ARM的SPI 的时钟始终维持高电平啊。怎么才能有时钟信号输出???

Android 回收站视图更新

为啥新连接的观察者会触发两次 LiveData 观察者

为啥观察 LiveData 时不调用 onChanged()