Issue
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
}
}
Solution
No, never use any references of Fragment/Activity ever in Viewmodel, mainly for two reasons
- The lifecycle of
viewmodelis different to that offragmentmeaning it reuses same instance ofviewmodelonfragmentbeing recreated due to config changes! In your case even if you hold the references inViewmodelthose references are dead objects and could potentially create leaks, as even theviewmodelis 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.
Answered By - Rajan Kali
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.