3

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?

MvG
  • 57,380
  • 22
  • 148
  • 276
  • Example I discovered recently: https://speakerdeck.com/cyrilmottier/crafting-custom-android-views?slide=38 – zapl Sep 25 '13 at 12:25
  • @zapl: Don't see that applying to my case: I don't think I know a `BaseSavedState` class fit for this purpose here. That seems nice if you control the whole hierarchy of views, but I don't. I can agree more with [slide 40](https://speakerdeck.com/cyrilmottier/crafting-custom-android-views?slide=40): that looks like my approach, with `superState` as the member I suggested. – MvG Sep 25 '13 at 12:41
  • Those slides belong together, on slide 40 you return an instance of your own Parcellable from the first slide – zapl Sep 25 '13 at 12:44
  • @zapl: Well, slide 38 doesn't describe `getSuperState`. Or phrades differently: passing an opaque `Parcelable` of the super class to the ctor of my own `Parcelable` makes sense, as does requesting a super `Parcelable` from it when restoring. Having that ctor argument passed to that of some known base class feels weird, and a `getSuperState` method which returns `this` (as I'd expect in this case) wouldn't work in my case either afaics. – MvG Sep 25 '13 at 13:08
  • 1
    That's from the existing [`View.BaseSavedState`](http://developer.android.com/reference/android/view/View.BaseSavedState.html) class. Related: http://stackoverflow.com/questions/14891434/overriding-view-onsaveinstancestate-and-view-onrestoreinstancestate-using-vi - There is only 1 parcel which contains the information for everything, the super class reads only the part that belongs to it and you read the part that belongs to you. That's why the parcel / the contained super class saved state is used in the method. – zapl Sep 25 '13 at 13:11
  • @zapl: Completely missed that nested class. Thanks a lot, looking at `View.BaseSavedState` and its ancestor `AbsSavedState`, this makes a lot more sense. Do you want to post this as an answer? – MvG Sep 25 '13 at 13:23

1 Answers1

5

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.

MvG
  • 57,380
  • 22
  • 148
  • 276