ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders

Introduction

In the last blog post I explored a simple use case with the new ViewModel class for saving basketball score data during a configuration change. ViewModels are designed to hold and manage UI-related data in a life-cycle conscious way. ViewModels allow data to survive configuration changes such as screen rotations.

At this point, you might have a few questions about the breadth of what ViewModels do. In this post I’ll be answering:

UPDATED 5/15/2019 for the new ViewModel Saved State module. Note that the section “How do I use ViewModels to save and restore UI state efficiently?” has an update at the top for the ViewModel Saved State module currently in alpha.

Do ViewModels persist my data?

TL;DR No. Persist as normal!

ViewModels hold transient data used in the UI but they don’t persist data. Once the associated UI Controller (fragment/activity) is destroyed or the process is stopped, the ViewModel and all the contained data gets marked for garbage collection.

Data used over multiple runs of the application should be persisted like normal in a local database, Shared Preferences, and/or in the cloud. If you want the user to be able to put the app into the background and then come back three hours later to the exact same state, you should also persist data. This is because as soon as your activity goes into the background, your app process can be stopped if the device is running low on memory. There’s a handy table in the activity class documentation which describes in which activity lifecycle states your app is stoppable:

Activity lifecycle documentation

As a reminder, when an app processes is stopped due to resource constraints, it’s stopped without ceremony and no additional lifecycle callbacks are called. This means that you can’t rely on [onDestroy](https://developer.android.com/reference/android/app/Activity.html#onDestroy()) being called. You do not have a chance to persist data at the time of process shutdown. Therefore, if you want to be the most sure that you won’t lose data, persist it as soon as the user enters it. This means that even if your app process is shut down due to resource constraints or if the device runs out of battery, the data will be saved. If you’re willing to concede losing data in instances of sudden device shutdown, you can save the data in the [onStop()](https://developer.android.com/reference/android/app/Activity.html#onStop()) callback, which happens right as the activity is going into the background.

Are ViewModels a replacement for onSaveInstanceState?

TL;DR No, but they are related so keep reading.

To understand the subtleties of this difference, it’s helpful to understand the difference between [onSaveInstanceState()](https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle,%20android.os.PersistableBundle)) and [Fragment.setRetainInstance(true)](https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean))

onSaveInstanceState(): This callback is meant to retain a small amount of UI related data in two situations:

onSaveInstanceState() is called in situations in which the activity is stopped, but not finished, by the system. It is not called when the user explicitly closes the activity or in other cases when [finish()](https://developer.android.com/reference/android/app/Activity.html#finish()) is called.

Note that a lot of UI data is automatically saved and restored for you:

“The default implementation of this method saves transient information about the state of the activity’s view hierarchy, such as the text in an EditText widget or the scroll position of a ListView widget.” — Saving and Restoring Instance State Documentation

These are also good examples of the type of data that is meant to be stored in onSaveInstanceState(). onSaveInstanceState() is not designed to store large amounts of data, such as bitmaps.onSaveInstanceState() is designed to store data that is small, related to the UI and not complicated to serialize or deserialize. Serialization can consume lots of memory if the objects being serialized are complicated. Because this process happens on the main thread during a configuration change, it needs to be fast so that you don’t drop frames and cause visual stutter.

Fragment.setRetainInstance(true): The Handling Configuration Changes documentation describes a process for storing data during a configuration change using a retained fragment. This sounds less useful than onSaveInstanceState() which covers both configuration changes as well as process shutdown. The usefulness of creating a retained fragment is that it’s meant to retain large sets of data such as images or to retain complex objects like network connections.

ViewModels only survive configuration change-related destruction; they do not survive the process being stopped. This makes ViewModels a replacement for using a fragment with setRetainInstance(true) (in fact ViewModels use a fragment with setRetainInstance set to true behind the scenes).

Additional ViewModel benefits

ViewModels and onSaveInstanceState() address UI data in very different ways. onSaveInstanceState() is a lifecycle callback, whereas ViewModels fundamentally change the way UI data is managed in your app. Here are a few more thoughts on the benefits of using ViewModel in addition to onSaveInstanceState():

How do I use ViewModels to save and restore UI state efficiently?


UPDATED 5/15/2019

T̶L̶;̶D̶R̶ ̶Y̶o̶u̶ ̶u̶s̶e̶ ̶a̶ ̶c̶o̶m̶b̶i̶n̶a̶t̶i̶o̶n̶ ̶o̶f̶ ̶V̶i̶e̶w̶M̶o̶d̶e̶l̶s̶,̶ ̶o̶n̶S̶a̶v̶e̶I̶n̶s̶t̶a̶n̶c̶e̶S̶t̶a̶t̶e̶(̶)̶ ̶a̶n̶d̶ ̶l̶o̶c̶a̶l̶ ̶p̶e̶r̶s̶i̶s̶t̶e̶n̶c̶e̶.̶

TL;DR You use ViewModels and the ViewModel Saved State module alongside local persistence.

There’s a new ViewModel Saved State module currently in alpha that you should be aware of. The point of this module is to essentially replace code that would have gone into the onSaveInstanceState callback and move it into the ViewModel. When it becomes stable, this will be the recommended way to save UI state using a ViewModel. Everything in this section is the “current, soon to be old” way of handling saved state with ViewModel.

Learn more

The dependency to add is:

androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha01

For instructions on how to use the new module, check out the documentation. There’s also a step-by-step example in the lifecycles codelab. The solution code from the codelab has a short example of the module in action; you can take a look here.

Finally, in the What’s New in Architecture Components (Google I/O’ 19) presentation, Sergey shows an example of the old way to handle ViewModel and onSaveInstanceState, and then the new way, using ViewModel Saved State. You can start watching the code example from 15:27. For an explanation of the whole saved state/ViewModel conundrum, start watching from 11:26.

What’s changed

Here’s an overview of what’s different:

Below is the “old way” that appeared when I first published this blog post. The advice still holds true, except that any mention of using onSaveInstanceState in the Activity should be swapped with SavedStateHandle in the ViewModel. With that in mind, read on.


It’s important that your activity maintains the state a user expects, even as it is rotated, shut down by the system or restarted by the user. As I just mentioned, it’s also important that you don’t clog up onSaveInstanceState with complex objects. You also don’t want to reload data from the database when you don’t need to. Let’s look at an example of an activity that allows you to search through your library of songs:

Example of the clean state of the activity and the state after a search

There are two general ways a user can leave an activity, and two different outcomes the user will expect:

To implement this behavior in both situations, you will use local persistence, ViewModels and onSaveInstanceState() together. Each will store different data the activity uses:

In the song search example, here’s how different events should be handled:

When the user adds a song — The ViewModel will immediately delegate persisting this data locally. If this newly added song is something that should be shown in the UI, you should also update the data in ViewModel to reflect the addition of the song. Remember to do all database inserts off of the main thread.

When the user searches for a song — Whatever complex song data you load from the database for the UI Controller should be immediately stored in the ViewModel. You should also save the search query itself in the ViewModel.

When the activity goes into the background and the activity is stopped by the system — When the activity goes into the background, onSaveInstanceState() will be called. You should save the search query in the onSaveInstanceState() bundle. This small amount of data is easy to save. It’s also all the information you need to get the activity back into its current state.

When the activity is created — There are three different ways this could happen:

This is one sane way to handle saving and restoring activity state. Depending on your activity implementation, you might not need to use onSaveInstanceState() at all. For example, some activities don’t open in a clean state after the user closes them. Currently, when I close and re-open Chrome on Android, it takes me back to the exact webpage I was looking at before closing it. If your activity behaves this way, you can ditch onSaveInstanceState() and instead persist everything locally. In the song searching example, that would mean persisting the most recent query, for example, in Shared Preferences.

Additionally, when you open an activity from an intent, the bundle of extras is delivered to you on both configuration changes and when the system restores an activity. If the search query were passed in as an intent extra, you could use the extras bundle instead of the onSaveInstanceState() bundle.

In both of these scenarios, though, you’d still use a ViewModel to avoid wasting cycles reloading data from the database during a configuration change!

Are ViewModels a replacement for Loaders?

TL;DR. Yes, ViewModels used in conjunction with a few other classes can replace Loaders.

Loaders are for loading data for UI Controllers. In addition, Loaders can survive configuration changes, if, for example, you rotate the device in the middle of a load. This sounds familiar!

A common use case for Loaders, in particular CursorLoaders, is to have the Loader observe the content of a database and keep the data the UI displays in sync. Using a CursorLoader, if a value in the database changes, the Loader will automatically trigger a reload of the data and update the UI.

ViewModels, used with other Architecture Components, LiveData and Room, can replace Loaders. The ViewModel ensures that the data can survive a configuration change. LiveData ensures that your UI can update when the data updates. Room ensures that when your database updates, your LiveData is notified.

Loaders are implemented as callbacks within your UI Controller, so an added benefit of ViewModels is they detangle your UI Controller and data loading. This makes you have fewer strong references between classes.

There are a few approaches to using ViewModels and LiveData to load data:

“Repository modules are responsible for handling data operations. They provide a clean API to the rest of the app. They know where to get the data from and what API calls to make when data is updated. You can consider them as mediators between different data sources (persistent model, web service, cache, etc.).” — Guide to App Architecture

Conclusion and further learning

In this post, I answered a few questions about what the ViewModel class is and what it’s not. The key takeaways are:

Want more ViewModel-ly goodness? Check out:

The architecture components were created based on your feedback. If you have questions or comments about ViewModel or any of the architecture components, check out our feedback page. Questions about this series? Leave a comment!