Activity, Fragment, Shared ViewModel and the Game of Life-cycle
In this post, we’ll see how crucial it is to define a suitable ViewModelStore owner and if you don’t do it right, there’ll be consequences.
Prerequisites — Activity and Fragment life-cycle, Back stack, Introduction to ViewModels.
Let’s dive into it. Shall we?
So, I’ve created a MainActivity, which looks something like this:
activity_main.xml contains a FrameLayout and two buttons for attaching fragments, which looks something like this:
I also have two fragments, that contain very similar code, something like below:
And finally, the ViewModel class, which we intend to share between the activity and fragments, looks like this:
Oh, and don’t forget to add click events for the buttons we added:
Let’s run this code and upon app-launch, attach FragmentA to the activity by tapping fragmentA_btn:
Notice that there are two different instances of SharedViewModel for MainActivity and FragmentA. But wait? If there are two different instances of SharedViewModel, how can we say that it is shared? Well, because it isn’t. Let’s replace FragmentA with FragmentB by tapping fragmentB_btn:
uh-oh, we have a problem…
There are 3 different instances of SharedViewModel that has been created now… So much for Shared ViewModel eh?
What went wrong?
Like I said in the beginning, it’s all a game of Life-cycle.
Take a good look at how we are creating the instance of SharedViewModel in the activity and the fragments.
ViewModelProvider(this).get(SharedViewModel::class.java)
ViewModelProvider constructor takes ViewModelStore owner as the only argument. So, the ViewModelStore owner that we passed to ViewModelProvider in the activity and in the fragments were all different. Hence, we got three different instances of SharedViewModel. But how do we pass the same ViewModelStore owner in this case for both activity and fragments?
That’s simple.
Fragments are attached to the activity. So, why not pass activity context as ViewModelStore owner in both the fragments. That should solve our problem right?
//Replace this statement with the statement below in fragments
ViewModelProvider(this).get(SharedViewModel::class.java)ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
The requireActivity() returns the context of the Activity to which the fragment is attached. Make sure this statement executes after the onAttach(Context context) method of Fragment.
Let’s see whether it works:
Damn right it does! All of them have the same instance of SharedViewModel now… Your fragments can now communicate with each other.
Conclusion
In this post, we learned how to share a ViewModel between activity and fragments and the importance of ViewModelStore owner. I have not used the best practises for this illustration for obvious reasons.
Happy reading :)