Saving and Restoring the State of an Android Activity

From Techotopia
Revision as of 15:21, 10 January 2019 by Neil (Talk | contribs)

Jump to: navigation, search
PreviousTable of ContentsNext
Android Activity State Changes by ExampleUnderstanding Android Views, View Groups


You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book


If the previous few chapters have achieved their objective, it should now be a little clearer as to the importance of saving and restoring the state of a user interface at particular points in the lifetime of an activity.

In this chapter, we will extend the example application created in Android Activity State Changes by Example to demonstrate the steps involved in saving and restoring state when an activity is destroyed and recreated by the runtime system.

A key component of saving and restoring dynamic state involves the use of the Android SDK Bundle class, a topic that will also be covered in this chapter.


Contents


Saving Dynamic State

An activity, as we have already learned, is given the opportunity to save dynamic state information via a call from the runtime system to the activity’s implementation of the onSaveInstanceState() method. Passed through as an argument to the method is a reference to a Bundle object into which the method will need to store any dynamic data that needs to be saved. The Bundle object is then stored by the runtime system on behalf of the activity and subsequently passed through as an argument to the activity’s onCreate() and onRestoreInstanceState() methods if and when they are called. The data can then be retrieved from the Bundle object within these methods and used to restore the state of the activity.

Default Saving of User Interface State

In the previous chapter, the diagnostic output from the StateChange example application showed that an activity goes through a number of state changes when the device on which it is running is rotated sufficiently to trigger an orientation change.

Launch the StateChange application once again, this time entering some text into the EditText field prior to performing the device rotation (on devices or emulators running Android 9 it may be necessary to tap the rotation button in the located in the status bar to complete the rotation). Having rotated the device, the following state change sequence should appear in the Logcat window:

onPause
onStop
onSaveInstanceState
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

Clearly this has resulted in the activity being destroyed and re-created. A review of the user interface of the running application, however, should show that the text entered into the EditText field has been preserved. Given that the activity was destroyed and recreated, and that we did not add any specific code to make sure the text was saved and restored, this behavior requires some explanation.

In actual fact most of the view widgets included with the Android SDK already implement the behavior necessary to automatically save and restore state when an activity is restarted. The only requirement to enable this behavior is for the onSaveInstanceState() and onRestoreInstanceState() override methods in the activity to include calls to the equivalent methods of the super class:

@Override
protected void onSaveInstanceState(Bundle outState) {
   super.onSaveInstanceState(outState);
}
 
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
   super.onRestoreInstanceState(savedInstanceState);
}

The automatic saving of state for a user interface view can be disabled in the XML layout file by setting the android:saveEnabled property to false. For the purposes of an example, we will disable the automatic state saving mechanism for the EditText view in the user interface layout and then add code to the application to manually save and restore the state of the view.

To configure the EditText view such that state will not be saved and restored in the event that the activity is restarted, edit the content_state_change.xml file so that the entry for the view reads as follows (note that the XML can be edited directly by clicking on the Text tab on the bottom edge of the Layout Editor panel):

<EditText
    android:id="@+id/editText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ems="10"
    android:inputType="text"
    android:saveEnabled="false"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

After making the change, run the application, enter text and rotate the device to verify that the text is no longer saved and restored before proceeding.


The Bundle Class

For situations where state needs to be saved beyond the default functionality provided by the user interface view components, the Bundle class provides a container for storing data using a key-value pair mechanism. The keys take the form of string values, while the values associated with those keys can be in the form of a primitive value or any object that implements the Android Parcelable interface. A wide range of classes already implements the Parcelable interface. Custom classes may be made “parcelable” by implementing the set of methods defined in the Parcelable interface, details of which can be found in the Android documentation at:

https://developer.android.com/reference/android/os/Parcelable.html

The Bundle class also contains a set of methods that can be used to get and set key-value pairs for a variety of data types including both primitive types (including Boolean, char, double and float values) and objects (such as Strings and CharSequences).

For the purposes of this example, and having disabled the automatic saving of text for the EditText view, we need to make sure that the text entered into the EditText field by the user is saved into the Bundle object and subsequently restored. This will serve as a demonstration of how to manually save and restore state within an Android application and will be achieved using the putCharSequence() and getCharSequence() methods of the Bundle class respectively.

You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book

Saving the State

The first step in extending the StateChange application is to make sure that the text entered by the user is extracted from the EditText component within the onSaveInstanceState() method of the StateChangeActivity activity, and then saved as a key-value pair into the Bundle object.

In order to extract the text from the EditText object we first need to identify that object in the user interface. Clearly, this involves bridging the gap between the Java code for the activity (contained in the StateChangeActivity.java source code file) and the XML representation of the user interface (contained within the content_state_change.xml resource file). In order to extract the text entered into the EditText component we need to gain access to that user interface object.

Each component within a user interface has associated with it a unique identifier. By default, the Layout Editor tool constructs the ID for a newly added component from the object type. If more than one view of the same type is contained in the layout the type name is followed by a sequential number (though this can, and should, be changed to something more meaningful by the developer). As can be seen by checking the Component Tree panel within the Android Studio main window when the content_state_change.xml file is selected and the Layout Editor tool displayed, the EditText component has been assigned the ID editText:

As3.0 statechange component tree.png


As outlined in the chapter entitled “The Anatomy of an Android Application”, all of the resources that make up an application are compiled into a class named R. Amongst those resources are those that define layouts, including the layout for our current activity. Within the R class is a subclass named layout, which contains the layout resources, and within that subclass is our content_state_change layout. With this knowledge, we can make a call to the findViewById() method of our activity object to get a reference to the editText object as follows:

final EditText editText = (EditText) findViewById(R.id.editText);

Having either obtained a reference to the EditText object and assigned it to a variable, we can now obtain the text that it contains via the object’s getText() method, which, in turn, returns the current text:

CharSequence userText = editText.getText();

Finally, we can save the text using the Bundle object’s putCharSequence() method, passing through the key (this can be any string value but in this instance, we will declare it as “savedText”) and the userText object as arguments:

outState.putCharSequence("savedText", userText);

Bringing this all together gives us a modified onSaveInstanceState() method in the StateChangeActivity.java file that reads as follows (noting also the additional import directive for android.widget.EditText):

package com.ebookfrenzy.statechange;
 
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
android.support.v7.app.AppCompatActivity;
import  import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.util.Log;
import android.widget.EditText;
 
public class StateChangeActivity extends AppCompatActivity {
.
.
.
       protected void onSaveInstanceState(Bundle outState) {
              super.onSaveInstanceState(outState);
              Log.i(TAG, "onSaveInstanceState");
              
              final EditText editText =
                      (EditText) findViewById(R.id.editText);
              CharSequence userText = editText.getText();
              outState.putCharSequence("savedText", userText);
       }
.
.

Now that steps have been taken to save the state, the next phase is to ensure that it is restored when needed.

Restoring the State

The saved dynamic state can be restored in those lifecycle methods that are passed the Bundle object as an argument. This leaves the developer with the choice of using either onCreate() or onRestoreInstanceState(). The method to use will depend on the nature of the activity. In instances where state is best restored after the activity’s initialization tasks have been performed, the onRestoreInstanceState() method is generally more suitable. For the purposes of this example we will add code to the onRestoreInstanceState() method to extract the saved state from the Bundle using the “savedText” key. We can then display the text on the editText component using the object’s setText() method:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
   super.onRestoreInstanceState(savedInstanceState);
   Log.i(TAG, "onRestoreInstanceState");
 
   final EditText editText =
        (EditText) findViewById(R.id.editText);
 
   CharSequence userText =
                savedInstanceState.getCharSequence("savedText");
 
   editText.setText(userText);
}

Testing the Application

All that remains is once again to build and run the StateChange application. Once running and in the foreground, touch the EditText component and enter some text before rotating the device to another orientation. Whereas the text changes were previously lost, the new text is retained within the editText component thanks to the code we have added to the activity in this chapter.

Having verified that the code performs as expected, comment out the super.onSaveInstanceState() and super.onRestoreInstanceState() calls from the two methods, re-launch the app and note that the text is still preserved after a device rotation. The default save and restoration system has essentially been replaced by a custom implementation, thereby providing a way to dynamically and selectively save and restore state within an activity.

Summary

The saving and restoration of dynamic state in an Android application is simply a matter of implementing the appropriate code in the appropriate lifecycle methods. For most user interface views, this is handled automatically by the Activity super class. In other instances, this typically consists of extracting values and settings within the onSaveInstanceState() method and saving the data as key-value pairs within the Bundle object passed through to the activity by the runtime system.

State can be restored in either the onCreate() or the onRestoreInstanceState() methods of the activity by extracting values from the Bundle object and updating the activity based on the stored values.

In this chapter, we have used these techniques to update the StateChange project so that the Activity retains changes through the destruction and subsequent recreation of an activity.


You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book



PreviousTable of ContentsNext
Android Activity State Changes by ExampleUnderstanding Android Views, View Groups