👏 ViewModel Saved State Review 👏
The android framework has a curious case of killing any app that’s in the background for more than a few minutes.
Take this for example:
The RedditViewModel is responsible for fetching a post from Reddit while the calling activity/fragment is responsible for observing the redditPostData variable.
Once the post is fetched the activity’s observer is notified and we show it to the user.
This is all well and good until you put your app in the background and wait for a few minutes.
Bringing the app to the foreground you might notice the post is all gone and we’re back to the initial CLICK ME state.
It seems that process-death murdered the redditPostData along with a bunch of other stuff.
In the olden days before android MVVM the UI state and other important variables would be kept with
onSaveInstanceState() . Since viewmodels made an entrance, that has been sort of replaced in favor of using livedata.
The issue still exists though and is the sneaky cause of many bugs and crashes. ViewModel-SavedState is stable as of the end of January and is Google’s attempt at addressing the “just restart the app lol 🤣” crowd.
Add some dependencies first:
Source code for the project can be found here.
Dagger and other disasters
The RedditViewModel needs a SaveStateHandle to do the job of saving stuff on process death.
When working with Dagger one would normally use a ViewModelProvider.Factory that will be responsible for instantiating the ViewModel.
Since a SaveStateHandle is needed here, we’ll use AbstractSavedStateViewModelFactory instead.
RedditRepository is our own little class that can be instantiated in a dagger module or using the
Providing the activity is not too hard either it seems. 🤔
I guess now is the right time to tie these together.
Reddit driven development
The activity should be all ready to get a RedditViewModel after we inject the factory in it like we normally do.
Injecting stuff in an activity is done with this classic that probably has a million different variations I’m missing.
While this isn’t really a Dagger tutorial (as if anyone can fully understand Dagger), you would need an application component and custom scopes to make this work. Check the repo for the whole thing.
Or get your DI set up the way you like it.
What about the ViewModel
The RedditViewModel has changed slightly:
- SavedStateHandle is just a map with all the stuff we might save in there.
- On init we check if there’s anything saved in the SavedStateHandle. If we find something then process death has probably happened. Let’s update the redditPostData with that value and go back to the state we were.
- Once the post is fetched we store that value in the SavedStateHandle and update the livedata like normal.
When running the app this guy will pop up sooner or later:
Let’s try that then.
Give the app another run and everything should work. Try initiating process death after fetching the reddit post with the red button found in LogCat too. The UI state should not be lost upon bringing the app in the foreground.
You might have noticed that the object stored in the bundle is a fairly simple one. Anything stored inside SavedStateHandle should be simple and lightweight.
Big lists / complex objects that are important for the functionality of an app regardless of system initiated process-death should preferably be stored in a database (like Room for example!).
Or you can just restart the app I guess. 🤡