Skip to content

State saving

ZieIony edited this page Nov 18, 2019 · 1 revision

View's state should carry the information needed to restore the view's look. It shouldn't contain the application's state. For example, if you have a Button that is disabled while the application is loading something, that's the application's state. The Button shouldn't save its disabled state in this case. Android views save only a couple of things: EditText's text, CheckBox'es/RadioButton's checked state, ListView's selection, etc.

To save its state the view also needs to have a unique id set. Ids are used by views' parents to store states in a map. Having two or more views with the same id in one container will result in state loss and may cause IllegalArgumentException if the views are of different types.

You can also turn state saving on and off using android:saveEnabled and isSaveEnabled()/setSaveEnabled(). State saving is enabled by default.

The code

It's one of the things you don't really ever remember and always copy from other projects. This example comes from my CheckBox class. To save the state you need to override onSaveInstanceState() and write values you'd like to save to a Parcelable. The superclass may have stored some data already, so you have to take it and pass it on.

public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();

    SavedState ss = new SavedState(superState);

    ss.checked = checkedState;
    return ss;
}

The corresponding onRestoreInstanceState() reads the state from a Parcelable and sets it to the view. Remember to read values in the order of writing.

public void onRestoreInstanceState(Parcelable state) {
    SavedState ss = (SavedState) state;

    super.onRestoreInstanceState(ss.getSuperState());
    setChecked(ss.checked);
}

The last part is the custom SavedState class used to read and write the state. The class has a constructor to use with a state of the view's superclass, a constructor to read the state from a Parcel object and a static factory (the CREATOR field) used by the Android framework internally. The most interesting things happen in the Parcel constructor (value reading) and in the writeToParcel() method (value writing).

static class SavedState extends BaseSavedState {
    boolean checked;

    SavedState(Parcelable superState) {
        super(superState);
    }

    private SavedState(Parcel in) {
        super(in);
        checked = in.readBoolean();
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeBoolean(checked);
    }

    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

Hierarchy state

ViewGroup and its descendants also support saving their hierarchy states. In other words, these classes save their own state and the states of their children by dispatching save/restore state events to them. There's a couple of methods for that:

  • saveHierarchyState()/restoreHierarchyState() start state saving/restoring. These methods can be called to save/restore the state to/from a SparseArray<Parcelable>.
  • dispatchSaveInstanceState()/dispatchRestoreInstanceState() dispatch the events to the group's children.

Usually, there's no need to interfere with these methods. The ViewGroup's implementation is very simple (iterates over children) and is used without changes by layouts.

Clone this wiki locally