Class not found when unmarshalling: android.support.v7.widget.Toolbar$SavedState

后端 未结 4 749
悲哀的现实
悲哀的现实 2020-12-31 12:09

i am using Maps API to create a simple android app and i get a wierd error i can\'t solve. It usually happens when i rotate my device. I\'m using google services 8.4.0

相关标签:
4条回答
  • 2020-12-31 13:03

    I had a similar problem with my custom view and found this solution. I noticed that this crash occurred when I extended RecyclerView or AppCompatSpinner and needed to save the state. The crash will probably happen for other views as well.

    Basically the crash is caused by a bug in AbsSavedState as mentioned here. And the crash only occurs when the constructor of the SavedState is called without a class loader. It seemed like this was an old issue however I started getting crash reports for Android 9 and 10.

    So the fix was to change:

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

    to:

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

    Edit I was on the right track, but then I was looking at some Android code and found out that there was actually a constructor missing that caused the crash. So I had the following constructor for the SavedState:

    SavedState(Parcel in)
    {
        super(in);
        //...
    }
    

    And I needed to add the following:

    @RequiresApi(Build.VERSION_CODES.N)
    SavedState(Parcel in, ClassLoader loader)
    {
        super(in, loader);
        //...
    }
    

    And then I needed to change the creator:

    public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>()
    {
        @Override
        public SavedState createFromParcel(Parcel source, ClassLoader loader)
        {
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? new SavedState(source, loader) : new SavedState(source);
        }
    
        @Override
        public SavedState createFromParcel(Parcel source)
        {
            return createFromParcel(source, null);
        }
    
        public SavedState[] newArray(int size)
        {
            return new SavedState[size];
        }
    };
    
    0 讨论(0)
  • 2020-12-31 13:05

    Changing activity_map.xml to this worked, thanks again Mike.

    <?xml version="1.0" encoding="utf-8" ?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar" />
    
        <fragment xmlns:map="http://schemas.android.com/apk/res-auto"
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </fragment>
    </RelativeLayout>
    
    0 讨论(0)
  • 2020-12-31 13:08

    Upon rotation, your SupportMapFragment gets destroyed and recreated. Before it's destroyed, it writes its current state to a Parcel, to be used in restoring its state when recreated. The Fragment's saved state will include the state of its child Views, and since you've nested a Toolbar within it, it attempts to save and restore that, as well. The Toolbar class does not have an inner class SavedState necessary for that, so this process fails when trying to restore the Toolbar instance from the Parcel.

    The solution is to not nest the Toolbar - or any other View, for that matter - within the <fragment> element. Instead, pull the <include> out of the <fragment>, and put them both in another ViewGroup; for example, a vertical LinearLayout, or a RelativeLayout.

    0 讨论(0)
  • 2020-12-31 13:15

    I didn't like provided solutions as it imposed on my layouts and architecture.

    Here's what I did to make it work. If you look at your stacktrace the ClassNotFoundException is coming from the line on GoogleMaps. So if we just fix that, the issue is gone.

    GoogleMaps pukes/throw an error when the savedInstanceState has other items in it besides it's own. The solution is to just give GoogleMaps it's own dedicated bundle.

    // class property
    private static final String KEY_MAP_SAVED_STATE = "mapState";
    
    // class methods
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mapView = findMapView();  // make your own method here
        Bundle mapState = (savedInstanceState != null)
                ? savedInstanceState.getBundle(KEY_MAP_SAVED_STATE): null;
        mapView.onCreate(mapState);
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Bundle mapState = new Bundle();
        mapView.onSaveInstanceState(mapState);
        outState.putBundle(KEY_MAP_SAVED_STATE, mapState);
    }
    

    One thing to note is I'm not using the SupportMapFragment. I'm using a MapView directly. You may have to extend the SupportMapFragment so you can catch the hook methods and provide a blank/clean bundle

    0 讨论(0)
提交回复
热议问题