Saving (and Retrieving) Android Instance State - Intertech

Visit: Blog

Saving (and Retrieving) Android Instance State

This whitepaper is dedicated to explaining and exemplifying how Android (activity/fragment) state can be saved and restored. It provides information about the use of onSaveInstanceState( ) and onRestoreInstanceState( ) methods of the activity for saving and restoring state. Also, it gives a thorough explanation of a fragment's setRetainInstance( ) and getRetainInstance( ) methods for managing state. The setRetainInstance( ) and getRetainInstance( ) methods are replacements for the now deprecated onRetainNonConfigurationInstance( ) and getLastNonConfigurationInstance( ) activity methods. These later two methods were deprecated as of Android 3.2.

Why do we need to save instance state? Android activities have a lifecycle (for a diagram of this lifecycle see here). As an activity becomes partially hidden (paused) or fully hidden (stopped), possibly even destroyed, your applications need a way to keep valuable state (i.e. data) the activity has obtained (probably from user input) or created even after the activity has gone away.

As an example, an orientation change can cause your activity to be destroyed and removed from memory and then recreated again. Unless you avail yourself to certain state saving mechanisms, any data your activity has at the time of orientation change can be completely lost in this seemingly simple act.

How do we save activity instance state? The Activity class provides the first, and possibly easiest, means of saving and restoring activity state. The onSaveInstanceState( ) method is a callback method that is not shown on most Activity lifecycle diagrams. Nonetheless, this method is called by Android between the onPause( ) and onStop( ) methods.

Important side note: the API documentation explicitly states the onSaveInstanceState( ) method will be called before onStop( ) but makes no guarantees it will be called before or after onPause( ). In the testing I did on the Android Virtual Device and two actually devices, it did get called between onPause( ) and onStop( ), but the quote below suggests one should not take this for granted.

Consultants Who Teach

"If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause()." The onSaveInstanceState( ) method is passed a Bundle object as a parameter. Data you want saved for the activity should be placed in the Bundle. In loose terms, a Bundle is a custom Android hash map. Data you want saved for the activity (for its redisplay or recreation) are stored in the Bundle with a String key. The key allows the data to be retrieved back from the Bundle.

Saving State using onSaveInstanceState( ) Example As an example, say your activity has a Calendar object property. This property might be used to track and display the "start time" when a user opened the activity and started entering data (not a far-fetched idea given that some transactions must be completed within a certain amount of time or are discarded).

public class DataEntryActivity extends Activity { private Calendar startTime = Calendar.getInstance(); // ... the rest of the activity

}

2

Consultants Who Teach

To underscore the importance of saving an activity's state, what would happen if the orientation of the device were to change while the user is looking at this activity? Indeed, because the activity would be destroyed and recreated on an orientation change, a brand new instance of the activity is created and therefore a new "start time" would be created and displayed to the user on this orientation change. Yikes! You would want the start time to be maintained, even if the actual activity instance is not the same one that kicked off the data entry. To keep the start time state across activity lifecycle events (to include destroy and recreation), implement the onSaveInstanceState( ) method as follows:

@Override protected void onSaveInstanceState(Bundle state) {

super.onSaveInstanceState(state); state.putSerializable("starttime", startTime); }

The start time data is now saved under the key of "starttime" in the bundle. Again, this method will be called automatically by Android before onStop( ) and the serialized Calendar object will be saved in the bundle which gets maintained and used across any instance of this activity. Important side note: the bundle is for data managed by instances of the activity class and not across activity classes. In other words, you can not put state in the bundle of Activity A and have Activity B opened and expect to see the state of Activity A in its bundle. How do we retrieve saved state?

3

Consultants Who Teach

If you notice, the onCreate( ) lifecycle method of the Activity class is passed a Bundle object as a parameter. This is the same Bundle object that was used to save state data in the onSaveInstanceState( ) method as shown above. Android passes the Bundle to each instance of an activity it creates. So, you could programmatically reach into that Bundle object to retrieve state, like the Calendar object above, in the onCreate( ) method. The code example below (specifically lines 8-12) does exactly that.

1: @Override

2: protected void onCreate(Bundle bundle) {

3:

super.onCreate(bundle);

4:

Log.v(TAG, "Inside of onCreate");

5:

setContentView(R.layout.activity_data_entry);

6:

timeTV = (TextView) findViewById(R.id.time_tv);

7:

8:

if ((bundle != null)

9:

&& (bundle.getSerializable("starttime") != null)) {

10:

startTime = (Calendar) bundle

11:

.getSerializable("starttime");

12:

}

13: }

Import side note: notice the check for the bundle being null? Remember, the onCreate( ) method gets called each time the activity is created, to include the very first time the activity is created and before there was ever any state. So make sure you check for the bundle being null before trying to use it and extract state data. The bundle will be null on the first call to onCreate( ).

Alternately, use the onRestoreInstanceState( ) method to extract saved state from the Bundle and restore it into new instances of the activity. The onRestoreInstanceState( ) method is another callback lifecycle method that is also rarely seen in the activity lifecycle diagrams. The onRestoreInstanceState( ) method is automatically invoked by Android between the onStart( ) and the onResume( ) lifecycle methods.

4

Consultants Who Teach

So, in order to restore the state of your activity, simply implement the onRestoreInstanceState( ) method to have it retrieve and restore activity state. This allows you to separate creation code from state restoring code. The example code below restores the Calendar "start time."

@Override protected void onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState); Log.v(TAG, "Inside of onRestoreInstanceState"); startTime = (Calendar) savedInstanceState.getSerializable("starttime"); }

You'll notice that the check for null isn't necessary as Android will have created an empty Bundle object by the time the method is called even on the initial creation of the activity. What kind of state can be saved in the Bundle? As I indicated above, the Bundle object is a kind of simple Android hash map. So data is stored in key value pairs. The keys are always Strings. The values must be of android.os.Parcelable type. Parcelables are close to Java's Serializable, but not the same. In fact, the Android documentation is quick to point out that the Parcelable API "is not a general-purpose serialization mechanism." Without getting into all the gory details of the Parcelable type, suffice it to say that types of data that can be placed into the value side of a Bundle are listed in the table below.

Allowed Bundle Value Types boolean boolean[ ]

5

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download