使用 viewLifecycleOwner 作为 LifecycleOwner

Posted

技术标签:

【中文标题】使用 viewLifecycleOwner 作为 LifecycleOwner【英文标题】:Use viewLifecycleOwner as the LifecycleOwner 【发布时间】:2020-04-18 15:50:32 【问题描述】:

我有一个片段:

class MyFragment : BaseFragment() 

   // my StudentsViewModel instance
   lateinit var viewModel: StudentsViewModel

   override fun onCreateView(...)
        ...
   

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
       super.onViewCreated(view, savedInstanceState)

       viewModel = ViewModelProviders.of(this).get(StudentsViewModel::class.java)
       updateStudentList()
   

   fun updateStudentList() 
        // Compiler error on 'this': Use viewLifecycleOwner as the LifecycleOwner
        viewModel.students.observe(this, Observer 
            //TODO: populate recycler view
        )
    

在我的片段中,我有一个在 onViewCreated(...) 中启动的 StudentsViewModel 实例。

在,StudentsViewModelstudents 是一个LiveData

class StudentsViewModel : ViewModel() 
    val students = liveData(Dispatchers.IO) 
          ...
    

返回MyFragment,在函数updateStudentList() 中出现编译器错误,抱怨我传递给.observe(this, Observer...)this 参数是Use viewLifecycleOwner as the LifecycleOwner

为什么会出现此错误?如何摆脱它?

【问题讨论】:

【参考方案1】:

为什么会出现这个错误?

Lint 建议您使用片段视图的生命周期 (viewLifecycleOwner) 而不是片段本身的生命周期 (this)。 Google 的 Ian Lake 和 Jeremy Woods 在 this android Developer Summit presentation 中讨论了差异,而 Ibrahim Yilmaz 在 this Medium post 中介绍了差异。简而言之:

viewLifecycleOwner 与片段拥有(和失去)其 UI 的时间相关联(onCreateView()onDestroyView()

this 与片段的整体生命周期相关联(onCreate()onDestroy()),可能会更长

如何摆脱它?

替换:

viewModel.students.observe(this, Observer 
        //TODO: populate recycler view
    )

与:

viewModel.students.observe(viewLifecycleOwner, Observer 
        //TODO: populate recycler view
    )

在您当前的代码中,如果调用了onDestroyView(),但没有调用onDestroy(),您将继续观察LiveData,当您尝试填充不存在的RecyclerView 时可能会崩溃。通过使用viewLifecycleOwner,您可以避免这种风险。

【讨论】:

请注意,在 DialogFragment 的情况下,您仍应使用“this”(可能还有每个不返回 onCreateView 视图的片段。否则您将收到异常:IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView() @androiddeveloper 你还可以在 onViewCreated 及以后使用 lifeCycleOwner 吗? @jontro 很确定你可以。试试看,让我知道:) 如果您尝试在您创建自己的对话框的 DialogFragment 中访问 viewLifecycleOwner(覆盖 onCreateDialog 而不是 onCreateView),则会出现异常 是的,我同意你的观点,但关键问题是 livedata 可能会被多次观察。我只反对添加视图可能会崩溃。【参考方案2】:

viewLifeCycleOwner 是代表 Fragment 的 View 生命周期的 LifecycleOwner。在大多数情况下,这反映了 Fragment 本身的生命周期,但在分离的 Fragment 的情况下,Fragment 的生命周期可能比 View 本身的生命周期长得多。

当用户离开片段时,片段视图会被破坏,即使片段本身没有被破坏。这实质上创建了两个生命周期,片段的生命周期和片段视图的生命周期。引用片段的生命周期而不是片段视图的生命周期可能会在更新片段的视图时导致细微的错误。

【讨论】:

【参考方案3】:

而不是this 使用viewLifecycleOwner 来观察LiveData

viewModel.students.observe(viewLifecycleOwner, Observer 
    //TODO: populate recycler view
)

【讨论】:

以上是关于使用 viewLifecycleOwner 作为 LifecycleOwner的主要内容,如果未能解决你的问题,请参考以下文章

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

Fragment中使用viewLifecycleOwner/getActivity/this

Android 回收站视图更新

espresso 测试“无法访问主线程上的数据库”时出现房间数据库错误

类作为成员变量类型使用接口作为成员变量类型使用接口作为方法的参数或返回值使用

使用对象类型作为方法的参数和作为方法的返回值