Jetpack Navigation 点击返回 home fragment 时 onCreateView 被再次调用

发布时间: 2023-05-24 22:29:28 作者: 大象笔记

在写一个 Android App,结构是一个 MainActivity,两个 fragment 分别是 home list fragment / detail fragment。 点击 home 页的 FAB 添加按钮,跳转 detail 页。

疑惑

我不明白为何

home fragment -> edit/detail fragment, 再返回时,home fragment onCreateView 又被调用了一遍。

navController.navigate(R.id.nav_edit)

难道,默认不支持 back stack ?

类似的问题

https://stackoverflow.com/questions/54581071/fragments-destroyed-recreated-with-jetpacks-android-navigation-components/55039009#55039009

上面的问题和我一模一样

I understand that this is a good pattern used with LiveData and ViewModel to avoid using more memory than necessary, but in my case this is annoying because the list has a complex layout and inflating it is time and CPU consuming, also because I'll need to save the scroll position of the list and scroll again to the same position user left the fragment. It's possible but seems it should exists a better way.

用了 Viewmodel 就好了,确实 viewmodel 只被创建了一次。所以耗时逻辑还是要放在 viewmodel 中。

另一个类似的问题:

https://stackoverflow.com/questions/53484790/why-is-ondestroyview-in-a-fragment-called-immediately-after-navigating-with-jetp

Fragments on the back stack have their views destroyed, but the view state is saved and automatically restored when it comes to the top of the stack again (i.e., you hit the system back button). Only views with an android:id have their state saved and restored, so make sure any important views have an id.

很智能,这样只需要将数据库监听的操作放到 view model 里就行了。

真相在日志里

在 fragment 的各个声明周期函数中都加了日志,且增加一行日志,判断是否 destroy 也被触发了,只是 destroy view 是合理的,否则 viewmodel 应该也被销毁了。

// 首次进入 home fragment
2023-05-21 21:47:37.876  I  activity onViewCreated ------------------>
2023-05-21 21:47:38.081  I  Home ViewModel create  ------------------>
2023-05-21 21:47:38.084  I  home fragment onCreateView ------------------>
2023-05-21 21:47:38.084  I  home fragment onViewCreated ------------------>

// 进入 detail fragment
2023-05-21 21:53:00.839  I  home fragment onDestroyView ------------------>

// 按返回按钮,返回 home fragment
2023-05-21 21:53:03.427  I  home fragment onCreateView ------------------>
2023-05-21 21:53:03.428  I  home fragemtn onViewCreated ------------------>

经过测试,确实只调用了 home fragment 的 onDestroyView, 而没有调用 onDestroy.

所以 back stack 是支持的,但是可能为了内存优化,把 fragment view 销毁了,而 fragment 还在。

View Model 与 Fragment 生命周期的关系

View Model 和 Fragment 的生命周期是相互独立的,但它们之间有一些关联。

View Model 是为了在旋转设备或者配置更改时保存和管理数据而设计的。 View Model 的生命周期与 Activity 或 Fragment 不同,它会一直存在于内存中,直到最后一个持有它的组件(如 Activity 或 Fragment)被销毁才会被清除。

Fragment 的生命周期包括以下方法:

  1. onAttach():当 Fragment 被添加到 Activity 时调用。
  2. onCreate():当 Fragment 被创建时调用。
  3. onCreateView():当 Fragment 的布局被创建时调用。
  4. onActivityCreated():当与 Fragment 相关联的 Activity 完成 onCreate() 后调用。
  5. onStart():当 Fragment 变得可见时调用。
  6. onResume():当 Fragment 可交互时调用。
  7. onPause():当 Fragment 要去到后台(失去焦点)时调用。
  8. onStop():当 Fragment 不再可见时调用。
  9. onDestroyView():当与 Fragment 相关联的视图被销毁时调用。
  10. onDestroy():当 Fragment 被销毁时调用。
  11. onDetach():当 Fragment 与 Activity 分离时调用。

在这些方法中,如果需要使用 View Model 中的数据,可以通过在 Fragment 中获取 View Model 实例并观察数据变化来实现。 同时,可以在 Fragment 的 onDestroy() 方法中清除对 View Model 的引用,以防止内存泄漏。

哎...

本以为可以简单粗暴地脱离 ViewModel / Hilt / Room 来实现一个清爽的项目,没想到除了 Room 都用上了。

我是一名山东烟台的开发者,联系作者