Sharing Data Between Fragment Easier With SharedViewModel

June 19, 2022

SharedViewModel

In Android we can use our ViewModel as a sharedViewModel. The idea is that the viewmodel will attach to the lifecycle of an Activity and can be accessed in fragments that are hosted to that Activity. But before we learn further about sharedViewModel, it is good for us to refresh our knowledge about ViewModel first, so let’s start it!

ViewModel

The main reason Android created the ViewModel Class is to handle the loss of data because of the configuration changes of View (Activity, Fragment). In Android the View class has its own lifecycle, and because of that the View class is not a good place to store the data. That’s why we use ViewModel, which is responsible for preparing data for the UI because it has its own lifetime.

Activity and ViewModel Lifetime.

As we see in the picture above, the viewmodel has its own lifetime. viewmodel objects are scoped to the lifecycle when passed to the ViewModelProvider. The viewmodel remains in memory until the lifecycle scope goes away permanently. in the case of an activity, when it finishes, while in the case of a fragment, when it’s detached.

⚠️ Before we start to the code, it’s good to see first the app here SharedViewModelApp as it will be the example of the app we use in this article

Common Problem of Sharing Data Between Fragments

1. InputUserFragment 2. InputItemFragment

As we see in those two pictures above, those fragments are a form fragment that need to be fill to continue the process.

But what if we have fill the edittext of fragment no. 1 and press continue, so now we are in fragment no. 2 and fill the edittext also, but we want to change the previous data, then we go back to the fragment no. 1 and then we update the data and continue again to fragment no. 2. we expect that the data still there as before right? but sadly it won’t.

The data will be lost because of the fragment no. 2 already destroyed, and the edittext value is removed from the memory. The more detail flow is like the Image below


So for the solution maybe we will use the help of saveInstanceState, ActivityForResult, or Thread Bus for passing the data to the prev fragment. but what if we have like six fragment that should fill and keep the edittext data eventhough we navigating to other fragments? yes it will take a lot of code to keep the data between the fragments

so there is another solution, that is the sharedViewModel. I think it’s an easier and cleaner way to solve this case even we have a lot of fragments.

How to Use SharedViewModel

1. SingleActivity

So if we use the sharedViewModel, we will have to make a viewmodel that attaches to the activity lifecycle that those fragments are hosted on. In this case the activity is SingleActivity.

class SingleActivity: AppCompatActivity(){
...
private val sharedViewModel by viewModels<SingleViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
...
if(savedInstanceState != null){
sharedViewModel.user = savedInstanceState.getParcelable(USER)
sharedViewModel.item = savedInstanceState.getParcelable(ITEM)
}
...
}
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
outState.putParcelable(USER, sharedViewModel.user)
outState.putParcelable(ITEM, sharedViewModel.item)
}
...

As we see in the code above, the Activity will provide the viewmodel of SingleViewModel, so it will follow the lifecycle of the SingleActivity, so later the fragment that hosted to the SingleActivity can use it, and every fragment navigation will not clear the data from the SingleViewModel.

2. SingleViewModel

In the SingleViewModel we will save two variables (user, item) that we will carry to the ConfirmFragment to display both of the data.

class SingleViewModel: ViewModel() {
var user: User? = null
var item: Item? = null
}

3. Fragments that hosted to SingleActivity

In the fragment, to provide the SingleViewModel we can use the activityViewModels delegate. Under the hood, it will provide the viewmodel with the activity context when passed to the ViewModelProvider.

class InputUserFragment : Fragment() {
...
private lateinit var binding: FragmentInputUserBinding
private val sharedViewModel by activityViewModels<SingleViewModel>()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.etAge.setText(sharedViewModel.user?.age.toStringNumber())
binding.etName.setText(sharedViewModel.item?.name ?: "")
...
}
...

After that we can use or modify the sharedViewModel data from the fragment. The sharedViewModel will keep the data even we are navigating to the other fragment (that are hosted to the SingleActivity), until the SingleActivity is destroyed.

This is the example of how we assign value to the sharedViewModel in InputUserFragment

binding.btnNext.setOnClickListener {
sharedViewModel.user = User(
name = binding.etName.text.toString().trim(),
age = binding.etAge.text.toString().trim().toIntOrNull() ?: 0
)
...
}

And This is the example of how we use the value of the sharedViewModel in InputUserFragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.etAge.setText(sharedViewModel.user?.age.toStringNumber())
binding.etName.setText(sharedViewModel.item?.name ?: "")
...
}

so from those two code above, the edittext data will not lose because we set the data from the sharedViewModel in onViewCreated, and also the sharedViewModel will be updated when pressing continue

Take Care Of The Data From Process Death and Some Configuration Change

We know the Activity can be destroyed by the Android system when the Android needs more resources. It is called Process Death. We as a developer should be aware of this. In case of our sharedViewModel structure, we will lose our data if process death happen. But not only process death, I’ve once experienced losing sharedViewModel data after changing the permission when opening the app.

In my experience, there are several actions that will lead to losing our sharedViewModel data

  1. Minimize our app and open another app for a long time (Process Death)
    • The Android system will kill our app process because Android needs more memory and it will kill some app in the background.
  2. Change the permission in the settings of the current app when opening the app

How to Prevent Losing Data From Process Death And Some Configuration Change

In SingleActivity, we can save our sharedViewModel data to the saveInstanceState bundle by overriding the onSaveInstanceState function and save the sharedViewModel data to it.

override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
outState.putParcelable(USER, sharedViewModel.user)
outState.putParcelable(ITEM, sharedViewModel.item)
}

Then if there is any process death or configuration change, the onSaveInstanceState function will be called.

After the Process Death, if we open the activity again, the onCreate will be called and we will restore the data to the sharedViewModel from the savedInstanceState bundle.

override fun onCreate(savedInstanceState: Bundle?) {
...
if(savedInstanceState != null){
sharedViewModel.user = savedInstanceState.getParcelable(USER)
sharedViewModel.item = savedInstanceState.getParcelable(ITEM)
}
...

And that’s it. Great! We already handle the process death from our app and now we can use the sharedViewModel confidently. 🥳

To see the full code you can check this repository: SharedViewModelApp

I think that all of the articles about sharedViewModel, thank you for visiting, have a good day! 👋

For more information about SharedViewModel

  1. ViewModel Overview | Android Developers
  2. Shared ViewModel Across Fragments | Android Developers