Search code examples
androidoverridingandroid-viewparcelableinstancestate

Overriding onSaveInstanceState


I'm trying to come to grips with the onSaveInstanceState method in class View (not the one in class Activity). That method does return a Parcelable. I derived my own View from ViewGroup and overrode that method to save my own state. But when the state was to be saved I got an exception:

java.lang.IllegalStateException:
Derived class did not call super.onSaveInstanceState()

That is true enough, but simply calling that method doesn't seem enough to me. So how should I do this? If the method would get passed a Parcel to write to, I could simply pass that same parcel to the super class, so things would get written sequentially. But this is a return value.

Should I include this returned object as a member of my own Parcelable representation, and use Parcel.writeParcelable to marshal it along with my own data if needed? Or is there some better way to handle parent invocation and chaining of parcelable objects? If so, what class loader should I use when loading the instance state of the super class?


Solution

  • Since zapl didn't turn his comment into an answer, I'm doing so.

    is there some better way to handle parent invocation and chaining of parcelable objects?

    The canonical way to accomplish this is by having your own class for saved data derived from View.BaseSavedState, which in turn is derived from AbsSavedState. You can call the onSaveInstance handler of the parent class and pass the resulting object to the constructor of your own class. When restoring the data, getSuperState gives the instance aimed at the parent class.

    A typical code example could look like this:

    static class SavedState extends View.BaseSavedState {
      // include own data members here
      public SavedState(Parcelable superState) {
        super(superState);
      }
      private SavedState(Parcel in) {
        super(in);
        // read own data here
      }
      @Override public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        // write own data here
      }
      public static final Parcelable.Creator<SavedState> CREATOR =
          new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) { return SavedState(in); }
        public SavedState[] newArray(int size) { return new SavedState[size]; }
      };
    }
    
    @Override public Parcelable onSaveInstanceState() {
      SavedState state = new SavedState(super.onSaveInstanceState());
      // set data members here
      return state;
    }
    
    @Override public void onRestoreInstanceState(Parcelable parcelable) {
      SavedState state = (SavedState)parcelable;
      super.onRestoreInstanceState(state.getSuperState());
      // restore from data members here
    }
    

    The above was adapted from this presentation by Cyril Mottier, but should also be a close match to how designers intended the use of this class in general.

    Should I include this returned object as a member of my own Parcelable representation, and use Parcel.writeParcelable to marshal it along with my own data if needed?

    Although the mentioned described above seems to be preferred, behind the scenes it does rely on writeParcelable as well. So if there are reasons to not use that base class, simply calling writeParcelable to store the state of the super class should be fine.

    what class loader should I use when loading the instance state of the super class?

    The current implementation of AbsSavedState does use null as the class loader argument, causing the use of the default class loader. However, that line of code is marked with a FIXME comment, so it might change one day.