英文:
Should Child Fragments get referenced in the ViewModel or the Parent Fragment
问题
I have the below layout
-MainActivity
-ParentFragment
-MaterialButtonToggleGroup
-ChildFragmentA
-RecyclerView
-ChildFragmentB
-RecyclerView
当我按下切换按钮时,我需要获取对子片段的引用,以便我可以显示/隐藏每个子片段.beginTransaction().show(childFragmentB!!).hide(childFragmentA!!)
而不是替换它们。
我在Android世界中还处于早期阶段,我想知道将子片段的引用保存在ViewModel中还是保存在ParentFragment中哪个更好?除了额外的代码之外,各种方法是否有优缺点?
例如,在下面的Parent代码中,当子项被初始化时,我使用父项的属性并最终为每个子项分配一个tag
。当发生设备旋转时,我会检查savedInstanceState
和tag
是否为null以重新初始化它们。根据我的理解,我还可以使用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)
}
// 如果我使用这些而不是父项内部的属性,我将不需要检查和标记
var childFragmentA: ChildFragmentA? = null
var childFragmentB: ChildFragmentB? = null
}
Parent:
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 = initialize _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
}
}
英文:
I have the below layout
-MainActivity
-ParentFragment
-MaterialButtonToggleGroup
-ChildFragmentA
-RecyclerView
-ChildFragmentB
-RecyclerView
When I press a toggle button I need references to the childFragment so that I can show/hide each child fragment .beginTransaction().show(childFragmentB!!).hide(childFragmentA!!)
instead of replacing them
I'm early in the Android world and I would like to know is it better to keep references to the child in the ViewModel or in the ParentFragment? Outside of the extra code are there any pros or cons to each?
For example in the below Parent code when the children are initialized I use the properties from the Parent and eventually give each child a tag
. When a device rotation occurs I check if the savedInstanceState
& the tag
are null or not to reinitialize them. From my understanding I can also use onSaveInstanceState
(not included) to achieve the same thing but I can also use the properties inside the ViewModel because it isn't affected by the rotation.
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)
}
// If I use these instead of the properties inside the parent I won't need the checks and the tags
var childFragmentA: ChildFragmentA? = null
var childFragmentB: ChildFragmentB? = null
}
Parent:
class ParentFragment: Fragment() {
// _binding ...
// binding ...
private lateinit var myViewModel: MyViewModel
val childTagA = "childFragmentA"
val childTagB = "childFragmentB"
// Instead of using these would it be better to just use the properties inside the 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 = initialize _binding ...
if (savedInstanceState == null) {
myViewModel = ViewModelProvider(requireActivity())[MyViewModel::class.java]
}
if (savedInstanceState == null && childFragmentManager.findFragmentByTag(childTagA) == null && childFragmentManager.findFragmentByTag(childTagB) == null)
childFragmentA = ChildFragmentA() // Alternatively I can use 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
得分: 1
不要在Viewmodel
中使用任何关于Fragment/Activity
的引用,主要有两个原因:
viewmodel
的生命周期与fragment
的生命周期不同,这意味着当由于配置更改而重新创建fragment
时,viewmodel
的实例会被重用!在您的情况下,即使在Viewmodel
中保留了引用,这些引用也是无效的对象,并且可能会导致泄漏,因为即使viewmodel
被保留,前台中的片段/活动实例也会与您在viewmodel
中保留的实例不同。- 单元测试和共享view model的使用,最好将Android框架从viewmodel中隔离出来,以便在隔离的环境中更好地进行单元测试。
英文:
No, never use any references of Fragment/Activity
ever in Viewmodel
, mainly for two reasons
- The lifecycle of
viewmodel
is different to that offragment
meaning it reuses same instance ofviewmodel
onfragment
being recreated due to config changes! In your case even if you hold the references inViewmodel
those references are dead objects and could potentially create leaks, as even theviewmodel
is preserved the fragment/activity instances in foreground would be different than what you have preserved inviewmodel
- Unit testing and shared view model usage, it is always better to isolate the android framework out of viewmodel to better unit testing them in isolated environments.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论