Saving an Activity’s State when it’s Interrupted

Android developers soon become aware of the precarious existence of the Activities they code, with the Android operating system pausing, stopping or killing Activities as it sees fit (see the Android Lifecycle on the Android developer site and the article Testing Android’s Activity Lifecycle). Despite this users expect Applications to be robust, when the user returns to a screen, e.g. after taking a call, the screen is expected to be the same as when it was last seen, that includes any values entered into any fields.

The term state is used to describe what data an application has at a moment in time, what it is displaying and the work it is performing. For example a quiz App would know which questions have been asked, how many answers were correct or incorrect and which question was comming next. If a user running the App went and checked their email, on return to the quiz it would not be expected to have restarted. For the quiz to pick up where it left off the internal state of the quiz is preserved. Android has built in support for saving and restoring state, by overriding a couple of functions an Activity’s state can be preserved. Let us look at a simple App and its internal state. Try the following test with a single EditText in an Activity. Create a new project in Eclipse (FileNew – Android Project) and give it a name, we use Test here. Select the Build Target, Android 1.5 will do.  A Package Name is required, in the usual format, like biz.tekeye.teststate. Create Activity wll be checked, call the Activity to create main. Replace the code in the res/layout/main.xml file with the listing below (for tips on copying code see Copying Code from the Articles).

[code lang=”xml”]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>[/code]

Run it and type a number e.g. 100. Then turn the device from portrait to landscape (or landscape to portrait). On the emulator use Ctrl-F11.

Switching from Portrait to Landscape with no View Id

The entered number disappears. Now do the same again but this time give the EditText an id.

[code lang=”xml”]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="@+id/edittext1"
  android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>[/code]

Switching Potrait to Landscape with a View Id

This time the number is preserved. Switching the device from portrait to landscape causes Android to stop and restart the Activity, allowing Activities the opportunity to redraw a screen for the different dimensions. With stopping and starting an Activity a common occurrence users would be annoyed if input kept being lost. Android have a pair of methods called onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle) which are used by Views automatically to save their data. These methods only work if the Views with data can be identified, hence the need for the EditText to have an id. This method pair can be overridden in an Activity so that state variables not associated with input fields can also be saved and restored.

Consider the following simple Application which can be used to sum some numbers. An EditText is used to enter a number, a plus button adds the entered number to a total which is then displayed in the EditText. The next number can be entered, plus pressed again and the running total displayed. The total is set back to zero with the C (for clear) button. Here is the layout to replace the code in main.xml:

[code lang=”xml”]<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="@+id/edittext1"
android:layout_width="140dp"
android:layout_height="wrap_content"
android:textSize="25sp"/>
<Button android:id="@+id/plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+"
android:width="60dp"
android:textSize="25sp"/>
<Button android:id="@+id/clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C"
android:width="60dp"
android:textSize="25sp"/>
</LinearLayout>[/code]

And code to replace the class in the main.java file in the package under the src folder (the Imports for View, EditText and OnClickListener will also need to be added:

[code lang=”java”]public class main extends Activity {
float total; //store running total
EditText number; //display number to add and total
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
number = (EditText) findViewById(R.id.edittext1);
//Add
findViewById(R.id.plus).setOnClickListener(new OnClickListener(){
public void onClick(View arg0) {
try {
total += Float.parseFloat(number.getText().toString());
number.setText(Float.toString(total));
number.setSelection(0, number.getText().length());
} catch (Exception ex) {}
}
});
//Clear
findViewById(R.id.clear).setOnClickListener(new OnClickListener(){
public void onClick(View arg0) {
total=0f;
number.setText("");
}
});
}
}
[/code]

Run the program, press 2, then +, then 2 again and + and 4 is displayed.

Calculation One

Press C, or run the program again, press 2, then +, then 2 again. Before pressing + again rotate the device (Ctrl-F11 on the emulator) then press +, this time 2 is displayed.

Switching from Portrait to Landscape Losses Calculation

Because the Activity was closed and recreated the running total was lost. We can solve this by preserving the total state variable. Add the following code to the main Activity class, just before the last closing brace (notice the calls to the super class methods that must be performed).

[code lang=”Java”]@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat("TOTAL", total);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
total=savedInstanceState.getFloat("TOTAL");
}[/code]

Run the updated App and repeat the test and this time the running total is correctly displayed after the screen rotates.

Switching from Portrait To Landscape With Correct Calculation

It is strightforward to support saving state in Android, there are plenty of helpful methods on the Bundle class. Notice that the onCreate(Bundle) method also has access to the same Bundle, thus saving the need to code an onRestoreInstanceState(Bundle), however the code using Bundle in onCreate will need to check for null, for when onSaveInstanceState(Bundle) was not called (e.g. when a user exits an Application normally). In the example code here the overridden function onRestoreInstanceState(Bundle savedInstanceState) can be removed and replaced with this code at the end of the onCreate(Bundle savedInstanceState) function:

[code lang=”Java”]if(savedInstanceState!=null)
total=savedInstanceState.getFloat("TOTAL");[/code]

onSaveInstanceState(Bundle) and onPause()

Use onPause() to save data that should be preserved long term, use onSaveInstanceState(Bundle) for transient states. For example in a game the current score could be saved in onSaveInstanceState(Bundle) and only saved in onPause() if it was good enough for the High Score table, in which cause the High Score table (in SharedPreferences, a database table, or a file) is updated.

onSaveInstanceState\onRestoreInstanceState and the Android Lifecycle

The SDK documentation says that onSaveInstanceState(Bundle) may run just before or just after onPause(). As for onRestoreInstanceState(Bundle) that occurs after onStart() and before onResume().

Example Project

Download some code that covers this article ready for importing into an Android project. The code can also be accessed via the Android Example Projects page. See the article Move Android Code Between PCs Running Eclipse on how to import example code into an Eclipse project. A version of this article was produced for the Android Cookbook.

Leave a Reply

Your email address will not be published. Required fields are marked *

Human Verification: In order to verify that you are a human and not a spam bot, please enter the answer into the following box below based on the instructions contained in the graphic.