Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Keeping Data When the User Rotates the Device

Author: Ian Darwin
Published? true
FormatLanguage: WikiFormat

Problem:

When the user rotates the device, Android will normally destroy and re-create the current Activity. You want to keep some data across this destroy-re-create cycle, but all the fields in your activity are lost during it.

Solution:

There are several approaches. If all your data are either primitive types, Strings, or are Serializable, you can save them in onSaveInstanceState() into the Bundle that is passed in.

There is also a solution that lets you return a single arbitrary Object: Implement onRetainNonConfigurationInstance() in your activity to save some values; call getLastNonConfigurationInstance() near the end of your onCreate() to see if there is a previous saved value and, if so, assign your fields accordingly.

Discussion:

Using onSaveInstanceState()

This method allows you to save various types of data into the Bundle passed to onCreate and to onSaveInstanceState().

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        if (savedInstanceState != null) {
            myData = savedInstanceState.getFloatArray("mydata");
        } else {
            myData = new float[DATA_SIZE];
        }
    }
 
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBooleanArray("mydata", myData);
    }

Using onRetainNonConfigurationInstance()

The getLastNonConfigurationInstance() method's return type is Object, so you can return any value you want from it. You might want to create a Map or write an inner class to store the values in, but it's often easier just to pass a reference to the current Activity, e.g, using this.

        /** Returns arbitrary single token object to keep alive across
         * the destruction and re-creation of the entire Enterprise.
         */
        @Override
        public Object onRetainNonConfigurationInstance() {
                return this;
        }

The above method will be called when Android destroys your main activity. Suppose you wanted to keep a reference to another object that was being updated by a running Service, that is referred to by a field in your Activity. There might also be boolean to indicate if the service is active. In the above, we return a reference to the Activity, from which all of its fields can be accessed (even private fields, of course, since the outgoing and incoming Activity objects are of the same class). In my geotracking app JPSTrack, for example, I have a FileSaver class which accepts data from the Location Service; I want it to keep getting the location, and saving it to disk, in spite of rotations, rather than having to restart it every time the screen rotates. Rotation is unlikely if your device is anchored in a car dash mount (we hope), but quite likely if the person not driving, or a pedestrian, is taking pictures or other notes while geotracking.

After Android creates the new instance, if of course calls onCreate() to notify the new instance that it has been created. In onCreate you typically do constructor-like actions such as initializing fields and assigning event listeners. Well, you still need to do those, so leave them alone. Near the end of onCreate(), however, you will add some code to get the old instance, if there is one, and get some of the important fields from it. The code should look something like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    saving = false;
    paused = false;

    // lots of other initializations...

    // Now see if we just got interrupted by e.g., rotation
        Main old = (Main) getLastNonConfigurationInstance();
        if (old != null) {
            saving = old.saving;
            paused = old.paused;
            
            // this is the most important line: keep saving to same file!
            fileSaver = old.fileSaver;
            if (saving) {
                    fileNameLabel.setText(fileSaver.getFileName());
            }
        return;
        }

        // I/O Helper
       fileSaver = new GPSFileSaver(...);
}

The fileSaver object is the big one, the one we want to keep running, and not re-create every time. If we don't have an old instance, we create the fileSaver only at the very end of onCreate(), since otherwise we'd be creating a new one just to replace it with the old one, which is at least bad for performance.

When the onCreate method finishes, we hold no reference to the old instance, so it should be eligible for Java GC.

The net result is that the Activity appears to keep running nicely across screen rotations, despite the re-creation.

An alternative possibility is to set android:configChanges="orientation" in your AndroidManifest.xml, but this is a bit riskier.

See Also:

Android's Application Object as a "Singleton"

Download:

The source code for this project can be downloaded from http://projects.darwinsys.com/jpstrack.android.
GR8DAN 2011-09-16 08:05:37.177 onSaveState() (1st para of Solution and 1st heading in discussion) should be onSaveInstanceState(). Recipe "Normal Life-cycle Methods" doesn't exist, maybe link to recipe [[Preserving Activity State]] for now.