子Fragment引用应存放在ViewModel还是父Fragment中?
子Fragment引用存储:ViewModel vs 父Fragment
布局结构
-MainActivity -ParentFragment -MaterialButtonToggleGroup -ChildFragmentA -RecyclerView -ChildFragmentB -RecyclerView
需求场景
点击切换按钮时,通过beginTransaction().show(childFragmentB!!).hide(childFragmentA!!)的方式显示/隐藏子Fragment,而非替换Fragment实例。
核心疑问
作为Android开发新手,想知道将子Fragment的引用存放在ViewModel还是父Fragment中更好?除了代码量差异外,两者各有哪些优缺点?
当前我在父Fragment中用属性存储子Fragment引用,并为每个子Fragment设置了tag,设备旋转时通过检查savedInstanceState和tag来重新初始化或获取引用。了解到也可以用onSaveInstanceState实现类似效果,或者利用ViewModel不受配置变更影响的特性来存储引用。
ViewModel代码示例
class MyViewModel: ViewModel() { private var mutableList = MutableLiveData<ArrayList<String>>() val recyclerViewList: LiveData<ArrayList<String>> get() = mutableList fun addItemToMutableList(item: String) { mutableList.value?.add(item) } // 如果用这里的属性替代父Fragment中的属性,就不需要检查逻辑和tag var childFragmentA: ChildFragmentA? = null var childFragmentB: ChildFragmentB? = null }
父Fragment代码示例
class ParentFragment: Fragment() { // _binding 初始化代码... // binding 初始化代码... private lateinit var myViewModel: MyViewModel val childTagA = "childFragmentA" val childTagB = "childFragmentB" // 这里的属性是否换成ViewModel中的更好? var childFragmentA: ChildFragmentA? = null var childFragmentB: ChildFragmentB? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // _binding = 初始化代码... if (savedInstanceState == null) { myViewModel = ViewModelProvider(requireActivity())[MyViewModel::class.java] } if (savedInstanceState == null && childFragmentManager.findFragmentByTag(childTagA) == null && childFragmentManager.findFragmentByTag(childTagB) == null) { childFragmentA = ChildFragmentA() // 也可以用 myViewModel.childFragmentA = ChildFragmentA() childFragmentB = ChildFragmentB() childFragmentManager.beginTransaction() .add(binding.containerFrameLayout.id, childFragmentA!!, childTagA) .add(binding.containerFrameLayout.id, childFragmentB!!, childTagB) .hide(childFragmentB!!) .commit() } else { childFragmentA = childFragmentManager.findFragmentByTag(childTagA) as ChildFragmentA childFragmentB = childFragmentManager.findFragmentByTag(childTagB) as ChildFragmentB } return binding.root } }
两种方案的优缺点对比
1. 引用存放在父Fragment中
优点
- 符合组件职责:Fragment的实例管理属于父Fragment的职责范畴,引用放在父Fragment更贴合Android组件的分层逻辑
- 降低内存泄漏风险:父Fragment的生命周期与子Fragment绑定,当父Fragment被销毁时,子Fragment的引用会被一同回收;若用Activity绑定的ViewModel存储,父Fragment销毁后ViewModel仍持有引用,可能导致子Fragment无法被GC回收
- 依赖关系清晰:子Fragment的引用仅在父Fragment内部使用,不会混入ViewModel的数据层逻辑,避免职责混乱
缺点
- 需要处理配置变更:设备旋转等配置变更会导致父Fragment重建,必须通过
findFragmentByTag重新获取子Fragment引用,需要额外的检查逻辑 - 代码稍显繁琐:要维护tag常量和savedInstanceState的判断代码
2. 引用存放在ViewModel中
优点
- 无需处理配置变更:ViewModel不受配置变更影响,旋转后可直接获取之前保存的子Fragment引用,省去了savedInstanceState和tag的检查逻辑
- 代码更简洁:不用重复编写重建时的引用恢复代码
缺点
- 违反单一职责:ViewModel的核心作用是存储UI相关的数据,而非持有Fragment组件引用,这样会让ViewModel承担额外的视图管理职责,违背设计原则
- 内存泄漏风险高:如果ViewModel绑定的是Activity的生命周期,当父Fragment被销毁(比如从回退栈弹出),ViewModel仍会持有子Fragment的引用,导致子Fragment无法被回收,引发内存泄漏
- 生命周期不匹配:ViewModel的生命周期长于父Fragment,持有Fragment引用可能导致意外的视图状态问题(比如父Fragment销毁后,调用ViewModel中Fragment的方法会引发异常)
建议
优先选择将子Fragment引用存放在父Fragment中。虽然需要处理配置变更的逻辑,但符合组件职责划分,能有效避免内存泄漏,整体逻辑更清晰。如果觉得配置变更时的代码繁琐,可以封装一个工具方法简化通过tag获取子Fragment的流程,或者直接复用childFragmentManager.findFragmentByTag()的写法。
内容的提问来源于stack exchange,提问作者Lance Samaria
相关产品推荐
相关产品推荐

