The way in which you do, or do not, preserve UI state is a crucial part of the user experience. Whether the user rotates the device, the user restarts the app, or the system shuts down the app, it is important that your activity maintain the state a user expects.
In cases where the UI data to preserve is simple and lightweight, you might use
onSaveInstanceState() alone to
preserve your state data. In cases where you
have complex data that you want to preserve, you can use a combination of
ViewModel objects, the
and persistent local storage.
This page discusses each of these approaches.
Managing simpler cases: onSaveInstanceState()
callback is designed to store relatively small
amounts of data needed to easily reload the state of UI controller,
such as an activity or a fragment, if the system
stops and later recreates that controller. This callback is meant to
handle two situations:
- Due to memory constraints, the system kills the app’s process while the app is in the background.
- A configuration change, such as a screen rotation or change to input language, occurs.
As both of these cases imply,
invoked in situations
in which the activity is stopped, but not finished, by the system. For example,
if the user leaves the app for several hours, and the system ejects the
relevant process from memory, the system calls the default implementation of
to save each UI controller that has an ID. Later, when
the user returns to the app, the system
the activity from the saved state.
onSaveInstanceState() is not
called when the user explicitly closes the activity or in other cases when
The system automatically saves and restores a lot of UI data for you: The
default implementation of
information about the state of the activity’s view hierarchy, such as the text
EditText widget or the scroll position of a
ListView widget. You can also save
custom data into this bundle by overriding the
If you override this method to save additional information not captured by each
individual entity, you should call the default implementation unless you are
prepared to save the states of every entity yourself.
onSaveInstanceState() is not
designed to store large amounts of data, such as
bitmaps, or complex data structures that require lengthy serialization or
deserialization. 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, serialization can cause dropped frames and visual
stutter if it takes too long. Therefore, instead of using
for complex data structures, make sure to store such structures in local
persistent storage; it's a good idea to store the data as soon as it's created
to minimize the chance of losing it. Then, use
onSaveInstanceState() to store
unique IDs for each of these objects.
The next section of this document provides more details about preserving more complex data.
Managing more complex states: divide and conquer
When you have more complex data structures that you need to preserve when an activity ends, you can efficiently save and restore UI state by dividing the work among several types of storage mechanisms.
There are two general ways a user can leave an activity, leading to two different outcomes the user may expect:
The user completely closes the activity. A user can completely close the activity if they swipe the activity off of the Recents screen, navigate up from the activity, or back out of the activity. The assumption in these cases is that the user has permanently navigated away from the activity, and if they ever re-open the activity, they will expect to start from a clean state.
The user rotates the phone or puts the activity in the background and then comes back to it. For example, the user performs a search and then presses the home button or answers a phone call. When they return to the search activity, they expect to find the search keyword and results still there, exactly as before.
To implement the behavior for complex data structures
in either situation, you use local persistence,
ViewModel class, and the
together. Each of these approaches stores a different type of data used in the
- Local persistence: Stores all data you don’t want to lose if you open and
close the activity.
- Example: A collection of song objects, which could include audio files and metadata.
ViewModel: Stores in memory all the data needed to display the associated UI Controller.
- Example: The song objects of the most recent search and the most recent search query.
onSaveInstanceState(): Stores a small amount of data needed to easily reload activity state if the system stops and then recreates the UI Controller. Instead of storing complex objects here, persist the complex objects in local storage and store a unique ID for these objects in
- Example: Storing the most recent search query.
As an example, consider an activity that allows you to search through your library of songs. Here’s how different events should be handled:
When the user adds a song , the
ViewModel immediately delegates 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 the
ViewModel object to reflect
the addition of the song. Remember to do all database inserts off of the main
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
object. You should also save the search query itself in the
When the activity goes into the background,
the system calls
should save the search query in the
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.
Restoring complex states: reassembling the pieces
When it's time for the user to return to the activity, there are two possible scenarios for recreating the activity:
- The activity is recreated after having been stopped by the system. The
activity has the query saved in an
onSaveInstanceState()bundle, and should pass the query to the
ViewModelsees that it has no search results cached, and delegates loading the search results, using the given search query.
- The activity is created after a configuration change. The activity has
the query saved in an
onSaveInstanceState()bundle, and the
ViewModelalready has the search results cached. You pass the query from the
onSaveInstanceState()bundle to the
ViewModel, which determines that it already has loaded the necessary data and that it does not need to re-query the database.
Note: When an activity is initially created,
contains no data, and the
ViewModel object is empty. When you create the
ViewModel object, you pass an empty query, which tells the
that there’s no data to load yet. Therefore, the activity starts in an empty
Depending on your activity implementation, you might not need to use
onSaveInstanceState() at all.
For example, a browser might take the user back
to the exact webpage they were looking at before they exited the browser. If
your activity behaves this way, you can forego using
instead persist everything locally. In the song-searching example, that might
mean persisting the most recent query in Shared Preferences.
Additionally, when you open an activity from an intent, the bundle of extras is
delivered to the activity both when the configuration changes and when the
system restores the activity. If the search query were passed in as an intent
extra, you could use the extras bundle instead of the
In either of these scenarios, you’d still use a
ViewModel to avoid wasting
cycles reloading data from the database during a configuration change.