Managing State in an Android Activity

Intro

When Android activities are closed or hidden from view they can lose state information, this article describes how to maintain this information.

 

What is state information?

For an Android activity its state is the property values of the controls currently displayed and any other information which is required to get the activity back to its current condition.

For a simple form application this would just be the text values of the text boxes, the check state of the check boxes etc. For a more complicated application like a game you might need to record the positions, velocities, scores and other data in which case an object orientated approach would probably be better and you would need to store a master object in memory.

 

Activity Lifecycle and State Information

State information is lost when the activity is closed, this is probably fine if this occurs when you have designed it but your activity is sometimes closed automatically eg

  • When the activity is not currently displayed on screen it will often be closed by the Dalvik engine to free memory
  • When screen orientation is rotated the activity will be completely closed and destroyed then opened again

The loss of state information is a problem because it looks like your application has just been started and any information entered by the user has been lost.

For more information see the documentation on the Android Developers website for a detailed explanation of the activity lifecycle.

 

How to Store State Information

To solve the problem of lost state information any state information from the activity that needs preserving needs to be stored. There are two common places to store state information:

  1. The saved instance state bundle which can be written to in the handler of theonSaveInstanceState(Bundle) activity event and read out again in the onCreate(Bundle) activity event.
  2. The SharedPreferences for the application by writing to the in the onPause() activity event , or some other event, and reading them out again in the onCreate(Bundle) activity event.

 

Difference Between Storage Spaces

When deciding how to store your activity state information you need to consider how long you want to store the data for.

  1. To store data only for application lifetime (ie temporarily), use theonSaveInstanceState(Bundle) activity eventThis data will only be held in memory until the application is closed, the data will be available any time that this activity starts within the current lifetime of the application.Explanation: if data is stored here by activity A then the application shows a different activity or rotates the screen (hence closing A) and then returns to A the data can be retrieved to populate the controls. However if the application is closed and opened again the data will be gone and the controls will revert to their default values.

    Example of use: storing text typed in by user and selections making up an order, blog entry, message, etc…

  2. To store data between application instances (ie permanently) use SharedPreferencesThis data is written to the database on the device and is available all the time.Explanation: if data is stored here then it can always be retrieved to populate the controls if the activity is closed then reopened or even if the application is closed then opened again the data can be retrieved to populate the controls. Even if the device is turned off and then on again this data will be persisted.

    Example of use: storing user logons, preferences, settings etc…

 

Store State in State Bundle

Override the onSaveInstanceState(Bundle) activity event , get the data that you need to store and add it to the savedInstanceState bundle. In this example the current values of the UI controls are stored.

This event occurs when the activity is about to be closed.

[Code sample – Store State in State Bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();
  
  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();
  
  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();
  
  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);
      
  super.onSaveInstanceState(savedInstanceState);
}

Values are written to the savedInstanceState Bundle using the put**** methods.

 

Load State from State Bundle

In the activity’s onCreate(Bundle) event you can check for values stored in the savedInstanceState Bundle and if present use them to set the values of the controls again.

If there is no corresponding value for a control set it to a default value.

[Code sample – Load State from State Bundle]
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState
{
    super.onCreate(savedInstanceState);
    
  // Restore UI state from the savedInstanceState.
  if (savedInstanceState != null)
  {
    String strValue = savedInstanceState.getString("Name");
    if (strValue != null)
    {
      EditText oControl = (EditText)findViewById(R.id.txtName);
      oControl.setText(strValue);
    }
    
    strValue = savedInstanceState.getString("Email");
    if (strValue != null)
    {
      EditText oControl = (EditText)findViewById(R.id.txtEmail);
      oControl.setText(strValue);
    }
    
    CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
    chkTandC.setChecked(savedInstanceState.getBoolean("TandC"));
  }
}

Values are read from the savedInstanceState Bundle using the get**** methods.

 

Store State in SharedPreferences

The SharedPreferences can be accessed in any event handler of the activity in this example we are overriding the onPause() activity event to store the values before an activity is destroyed.

[Code sample – Store State in SharedPreferences]
@Override
protected void onPause() 
{
  super.onPause();
  
  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();
  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();
  
  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();
  
  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();
  
  editor.putString(“Name”, strName)// value to store
  editor.putString(“Email”, strEmail)// value to store
  editor.putBoolean(“TandC”, blnTandC)// value to store    
  // Commit to storage
  editor.commit();
}

To store values in the SharedPreferences you need to get an instance of an editor , use its put**** methods then call commit().

 

Load State from SharedPreferences

In the activity’s onCreate(Bundle) event you can check for values stored in the SharedPreferences , to read values there is no need to get an instance of an editor .

[Code sample – Load State from SharedPreferences]
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState
{
  super.onCreate(savedInstanceState);
  
  // Get the between instance stored values
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  
  // Set the values of the UI
  EditText oControl = (EditText)findViewById(R.id.txtName);
  oControl.setText(preferences.getString("Name"null));
  
  oControl = (EditText)findViewById(R.id.txtEmail);
  oControl.setText(preferences.getString("Email"null));
  
  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  chkTandC.setChecked(preferences.getBoolean("TandC"false));
}

Use the get**** methods to read the values, if there is no corresponding value for a control set it to a default value.

 

How to Store Object Instances

If you have an object instances that you need to keep alive in memory then you will need to use theonRetainNonConfigurationInstance() activity event to store them.

This is useful for more complicated applications which might be running processes in threads or be managing data in an object orientated way.

Like the use the onSaveInstanceState(Bundle) activity event method of storing state information this will be lost between application instances. If you need to maintain the state of objects between application runtimes then you may want to add methods to your objects to allow them to read and write their data to the device database, files on the SD card, to a remote storage location (perhaps via a webservice) or some other method.

Override the onRetainNonConfigurationInstance() activity event to store a reference to your object instance that will keep it in memory while the application is running.

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass;// Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance() 
{
  if (moInstanceOfAClass != null// Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

To check for and retrieve the instance that was stored use the getLastNonConfigurationInstance()method.

[Code sample – retrieve object intance]
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState
{
  super.onCreate(savedInstanceState);
   
  // Get the instance of the object that was stored
  // if one exists
  if (getLastNonConfigurationInstance() != null)
  {
    moInstanceOfAClass
(cMyClassType)getLastNonConfigurationInstance();
  }
}

Leave a Reply

You must be logged in to post a comment.