androidandroid-recyclerviewsavestate

Saving state custom RecyclerView


I made a custom RecyclerView in order to enable pagination in the lists of my application. I'm having a crash at restoring state. Maybe this crash is related to a known bug in the support library, but, teorically, it has been resolved in support library version 24.0.0: https://code.google.com/p/android/issues/detail?id=196430, but it is still crashing.

Maybe I'm doing something wrong? I'm having a BadParcelableException ClassNotFoundException when unmarshalling: android.support.v7.widget.RecyclerView$SavedStateHere trying to recreate the SavedState.

Here is my SavedState class inside the CustomRecyclerView controller:

public static class SavedState extends RecyclerView.BaseSavedState {

    public boolean isLastPage;
    public boolean isLoading;
    public int maxPages;
    public int pageSize;
    public int currentPage;
    public int firstVisibleItem;
    public Parcelable layoutManagerState;

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

    public SavedState(Parcel source) {
        super(source);
        this.isLastPage = source.readByte() == 1;
        this.isLoading = source.readByte() == 1;
        this.maxPages = source.readInt();
        this.pageSize = source.readInt();
        this.currentPage = source.readInt();
        this.firstVisibleItem = source.readInt();
        this.layoutManagerState = source.readParcelable(LinearLayoutManager.SavedState.class.getClassLoader());
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeByte((byte) (isLastPage ? 1 : 0));
        dest.writeByte((byte) (isLoading ? 1 : 0));
        dest.writeInt(maxPages);
        dest.writeInt(pageSize);
        dest.writeInt(currentPage);
        dest.writeInt(firstVisibleItem);
        dest.writeParcelable(layoutManagerState, flags);
    }

    public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
        @Override
        public SavedState createFromParcel(Parcel source) {
            return new SavedState(source);
        }

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

Here is where I'm saving and restoring the state in the controller:

public Parcelable saveState(Parcelable superState) {
    SavedState savedState       = new SavedState(superState);
    savedState.isLastPage       = this.isLastPage;
    savedState.isLoading        = this.isLoading;
    savedState.maxPages         = this.maxPages;
    savedState.pageSize         = this.pageSize;
    savedState.currentPage      = this.currentPage;
    savedState.firstVisibleItem = this.firstVisibleItemPosition;

    if (layoutManager != null) {
        savedState.layoutManagerState = layoutManager.onSaveInstanceState();
    }

    return savedState;
}

public Parcelable restoreState(SavedState savedState) {

    this.isLastPage                 = savedState.isLastPage;
    this.isLoading                  = savedState.isLoading;
    this.maxPages                   = savedState.maxPages;
    this.pageSize                   = savedState.pageSize;
    this.currentPage                = savedState.currentPage;
    this.firstVisibleItemPosition   = savedState.firstVisibleItem;

    return savedState.layoutManagerState;
}

And here is the onSaveInstanceStateand onRestoreInstanceState implementations in the custom RecyclerView:

    @Override
    protected Parcelable onSaveInstanceState() {

        return controller.saveState(super.onSaveInstanceState());
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {

        if (state instanceof EndlessRecyclerViewController.SavedState) {

            EndlessRecyclerViewController.SavedState savedState = (EndlessRecyclerViewController.SavedState) state;

            Parcelable layoutManagerState = controller.restoreState(savedState);

            getLayoutManager().onRestoreInstanceState(layoutManagerState);
            super.onRestoreInstanceState(savedState.getSuperState());
        }
        else
            super.onRestoreInstanceState(state);
    }

I also tried to extend the SavedState from support AbsSavedState and still crashing. And I'm not sure if I have to call the onSaveInstanceState of LinearLayoutManager...

Thanks!


Solution

  • Finally I solved using the AbsSavedState (so, I extend the SavedState class from it) from support library version 24.2.1. And changing the constructor that receives the Parcel object from:

    public SavedState(Parcel source) {
      super(source);
      //... 
    }
    

    to:

    public SavedState(Parcel source) {
      super(source, LinearLayoutManager.class.getClassLoader());
      //... 
    }