NeuroAgent

Android Save Instance State: Complete Guide

Learn how to properly save and restore Android activity state using onSaveInstanceState(). Complete guide with code examples and best practices for handling configuration changes.

Question

How can I properly save and restore an activity’s state using the Android save instance state mechanism?

I’m developing an Android application and having trouble with saving the application’s state. I’ve modified the basic ‘Hello, Android’ example as follows:

java
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

I expected this to work for the simplest case, but the activity always displays the initial “Welcome to HelloAndroid!” message regardless of how I navigate away from and back to the app.

I believe the solution involves overriding methods like onPause(), but I haven’t been able to find a clear explanation in the documentation after searching for about 30 minutes. What’s the proper way to save and restore an activity’s state in Android?

NeuroAgent

The Android save instance state mechanism requires you to override both onSaveInstanceState() to save your data and then properly handle the restored data in onCreate() or onRestoreInstanceState(). Your current code only checks for the saved state but never actually saves any state to restore, which is why you always see the initial message.

Contents


Understanding the Android State Saving Mechanism

Android’s save instance state mechanism is designed to handle temporary configuration changes and interruptions without losing important user data. When the system needs to destroy an activity (due to screen rotation, memory constraints, or user navigation), it automatically calls onSaveInstanceState() to persist critical data. This data is then passed back to the activity when it’s recreated.

The process works as follows:

  1. Save Phase: The system calls onSaveInstanceState(Bundle outState) before destroying the activity
  2. Destruction: The activity is destroyed
  3. Restore Phase: The activity is recreated, and the system passes the saved Bundle to onCreate(Bundle savedInstanceState) and onRestoreInstanceState(Bundle savedInstanceState)

Your current implementation only checks for the presence of savedInstanceState but never saves any data, so the Bundle is always null when the activity is recreated.


Proper Implementation of State Saving

Here’s the corrected version of your code with proper state saving and restoration:

java
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

    private TextView mTextView = null;
    private static final String KEY_MESSAGE = "message";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mTextView = new TextView(this);

        // Check if we have a saved state
        if (savedInstanceState == null) {
            // First time - create new state
            mTextView.setText("Welcome to HelloAndroid!");
        } else {
            // Restored state - restore previous message
            String savedMessage = savedInstanceState.getString(KEY_MESSAGE);
            mTextView.setText(savedMessage != null ? savedMessage : "Welcome back.");
        }

        setContentView(mTextView);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        
        // Save the current state of the activity
        String currentMessage = mTextView.getText().toString();
        outState.putString(KEY_MESSAGE, currentMessage);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        
        // Additional restoration can be done here if needed
        String savedMessage = savedInstanceState.getString(KEY_MESSAGE);
        if (savedMessage != null) {
            mTextView.setText(savedMessage);
        }
    }
}

Key improvements:

  1. Added onSaveInstanceState() to save the current text
  2. Used a constant key (KEY_MESSAGE) for reliable data storage
  3. Implemented proper null checks for restored data
  4. Added onRestoreInstanceState() as an alternative restoration point

Key Methods in the Activity Lifecycle

onSaveInstanceState(Bundle outState)

This method is called before the activity is potentially destroyed. It’s your opportunity to save data that should be restored if the activity is recreated.

java
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    // Save your data here
    outState.putString("key", "value");
    outState.putInt("counter", mCounter);
}

onCreate(Bundle savedInstanceState)

Called when the activity is first created or recreated. The savedInstanceState parameter contains the data saved in onSaveInstanceState().

java
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    if (savedInstanceState != null) {
        // Restore saved data
        String savedText = savedInstanceState.getString("key");
        int savedCounter = savedInstanceState.getInt("counter");
    }
}

onRestoreInstanceState(Bundle savedInstanceState)

Called after onStart() but before onResume() when the activity is being restored from a saved state.

java
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    // Additional restoration logic
    mTextView.setText(savedInstanceState.getString("key"));
}

Common Pitfalls and Best Practices

Common Mistakes

  1. Not calling super methods:

    java
    // WRONG - Always call super
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // Missing super call
        outState.putString("key", "value");
    }
    
    // CORRECT
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("key", "value");
    }
    
  2. Saving non-persistent data: Don’t save references to views, bitmaps, or other objects that can’t be serialized.

  3. Over-reliance on onSaveInstanceState: This is for temporary state only, not for long-term data persistence.

Best Practices

  1. Use constants for Bundle keys:

    java
    private static final String USER_ID_KEY = "user_id";
    private static final String USER_NAME_KEY = "user_name";
    
    outState.putInt(USER_ID_KEY, userId);
    outState.putString(USER_NAME_KEY, userName);
    
  2. Save complex data as primitives or simple objects:

    java
    // Save ArrayList as Parcelable
    outState.putParcelableArrayList("items", mItems);
    
    // Save custom objects as Parcelable
    outState.putParcelable("user", mUser);
    
  3. Handle null values gracefully:

    java
    String savedText = savedInstanceState != null ? 
        savedInstanceState.getString("key") : "default text";
    

Advanced State Management

Using ViewModel for Complex State

For more complex state management, consider using Android Architecture Components:

java
public class MyViewModel extends ViewModel {
    private MutableLiveData<String> currentText = new MutableLiveData<>();
    
    public LiveData<String> getCurrentText() {
        return currentText;
    }
    
    public void setText(String text) {
        currentText.setValue(text);
    }
}

// In Activity
private MyViewModel viewModel;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    
    // Observe data changes
    viewModel.getCurrentText().observe(this, text -> {
        mTextView.setText(text);
    });
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("current_text", viewModel.getCurrentText().getValue());
}

Handling Configuration Changes

For specific configuration changes where you want to keep the activity alive:

xml
<!-- AndroidManifest.xml -->
<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize"
    android:windowSoftInputMode="adjustResize" />

Using SharedPreferences for Persistent Data

For data that needs to survive app restarts:

java
// Save data
SharedPreferences prefs = getSharedPreferences("MyPrefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("key", "value");
editor.apply();

// Restore data
String savedValue = prefs.getString("key", "default");

Conclusion

  1. Always implement both saving and restoration: Override onSaveInstanceState() to save data and check savedInstanceState in onCreate() to restore it.

  2. Use proper Bundle keys: Define constants for your Bundle keys to avoid typos and ensure consistency.

  3. Handle null values gracefully: Always check if savedInstanceState is null before trying to access it.

  4. Choose the right storage mechanism: Use onSaveInstanceState() for temporary state, SharedPreferences for app-level persistence, and ViewModel for configuration changes.

  5. Call super methods: Always call the superclass implementation of lifecycle methods to ensure proper framework behavior.

By implementing these patterns correctly, your Android application will maintain its state properly across configuration changes and interruptions, providing a seamless user experience.

Sources

  1. Android Developers - Activity Lifecycle
  2. Android Developers - Saving Activity State
  3. Android Developers - Handling Configuration Changes
  4. Android Documentation - Bundle Class