Manually clearing an Android ViewModel?
Quick solution without having to use Navigation Component
library:
getActivity().getViewModelStore().clear();
This will solve this problem without incorporating the Navigation Component
library. It’s also a simple one line of code. It will clear out those ViewModels
that are shared between Fragments
via the Activity
If you check the code here you'll find out, that you can get the ViewModelStore
from a ViewModelStoreOwner
and Fragment
, FragmentActivity
for example implements, that interface.
Soo from there you could just call viewModelStore.clear()
, which as the documentation says:
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
N.B.: This will clear all the available ViewModels for the specific LifeCycleOwner, this does not allow you to clear one specific ViewModel.
I think I have a better solution.
As stated by @Nagy Robi, you could clear the ViewModel
by call viewModelStore.clear()
. The problem with this is that it will clear ALL the view model scoped within this ViewModelStore
. In other words, you won't have control of which ViewModel
to clear.
But according to @mikehc here. We could actually create our very own ViewModelStore
instead. This will allow us granular control to what scope the ViewModel have to exist.
Note: I have not seen anyone do this approach but I hope this is a valid one. This will be a really good way to control scopes in a Single Activity Application.
Please give some feedbacks on this approach. Anything will be appreciated.
Update:
Since Navigation Component v2.1.0-alpha02, ViewModel
s could now be scoped to a flow. The downside to this is that you have to implement Navigation Component
to your project and also you have no granualar control to the scope of your ViewModel
. But this seems to be a better thing.
As OP and Archie said, Google has given us the ability to scope ViewModel to navigation graphs. I will add how to do it here if you are using the navigation component already.
You can select all the fragments that needs to be grouped together inside the nav graph and right-click->move to nested graph->new graph
now this will move the selected fragments to a nested graph inside the main nav graph like this:
<navigation app:startDestination="@id/homeFragment" ...>
<fragment android:id="@+id/homeFragment" .../>
<fragment android:id="@+id/productListFragment" .../>
<fragment android:id="@+id/productFragment" .../>
<fragment android:id="@+id/bargainFragment" .../>
<navigation
android:id="@+id/checkout_graph"
app:startDestination="@id/cartFragment">
<fragment android:id="@+id/orderSummaryFragment".../>
<fragment android:id="@+id/addressFragment" .../>
<fragment android:id="@+id/paymentFragment" .../>
<fragment android:id="@+id/cartFragment" .../>
</navigation>
</navigation>
Now, inside the fragments when you initialise the viewmodel do this
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)
if you need to pass the viewmodel factory(may be for injecting the viewmodel) you can do it like this:
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }
Make sure its R.id.checkout_graph
and not R.navigation.checkout_graph
For some reason creating the nav graph and using include
to nest it inside the main nav graph was not working for me. Probably is a bug.
Source: https://medium.com/androiddevelopers/viewmodels-with-saved-state-jetpack-navigation-data-binding-and-coroutines-df476b78144e
Thanks, OP and @Archie for pointing me in the right direction.