Skip to main content

Android : AbsSavedState cannot be cast to $SavedState

Android AbsSavedState cannot be cast to <View>$SavedState

I came across a strange crash today.

This is the stacktrace :

Fatal Exception: java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to android.widget.ScrollView$SavedState
       at android.widget.ScrollView.onRestoreInstanceState(ScrollView.java:1810)
       at android.view.View.dispatchRestoreInstanceState(View.java:14768)
       at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3123)
       at android.view.View.restoreHierarchyState(View.java:14746)
       at android.support.v4.app.Fragment.restoreViewState(SourceFile:470)
       at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1326)
       at android.support.v4.app.FragmentManagerImpl.moveFragmentsToInvisible(SourceFile:2323)
       at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(SourceFile:2136)
       at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(SourceFile:2092)
       at android.support.v4.app.FragmentManagerImpl.execSingleAction(SourceFile:1969)
       at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(SourceFile:620)
       at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(SourceFile:166)
       at android.support.v4.view.ViewPager.populate(SourceFile:1268)
       at android.support.v4.view.ViewPager.setCurrentItemInternal(SourceFile:668)
       at android.support.v4.view.ViewPager.setCurrentItemInternal(SourceFile:630)
       at android.support.v4.view.ViewPager.setCurrentItem(SourceFile:622)

This crash was happening when the user minimizes our app and then opens any other heavy/memory-consuming app and then after some time, switches back to our app. At that time, the app crashes.

Now, one thing is clear here : that the system had killed our app due to low-memory situation and is trying to restore the state now. In this process, it’s not able to recreate a particular view and we can see from the topmost line of the stacktrace that this view is ScrollView.

Till this point, half of the problem has been diagnosed. The second half that why it’s not able to restore the state of that view is still pending.

After some investigation, I found out that this is because when it’s trying to restore, it performs findViewById on the list of all the views whose state had been saved and then whatever view it finds first, it tries to apply the saved state of that view to our scrollview. So, if there was any other view whose id was same as that of scrollview, chances are that system is trying to restore scrollview with the saved state of that view and therefore, we get ClassCastException.

One thing is clear here, the people at android need to print more useful information in the logs to debug this.

Anyways, after we’ve figured out the whole problem, the solution here is simple. Just make sure that no two views who might be in memory at the same time should have same id. If they are of different types, the app would crash in this case. And if they are of same type, you might get some inconsistent behaviour.


Comments

  1. Hi - I've seen this error. Thanks for the explaination.
    I am not sure how to make sure the viewIds are unique?

    I am using XamarinForms - how might I go about this?

    Fatal Exception: java.lang.ClassCastException: android.widget.AbsListView$SavedState cannot be cast to android.widget.ProgressBar$SavedState
    at android.widget.ProgressBar.onRestoreInstanceState(ProgressBar.java:2414)
    at android.view.View.dispatchRestoreInstanceState(View.java:21578)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4699)
    at android.view.View.restoreHierarchyState(View.java:21556)
    at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2231)
    at android.app.Activity.onRestoreInstanceState(Activity.java:1601)
    at android.app.Activity.performRestoreInstanceState(Activity.java:1556)
    at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1354)
    at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3488)
    at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
    at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
    at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2147)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:237)
    at android.app.ActivityThread.main(ActivityThread.java:7811)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)

    ReplyDelete
    Replies
    1. I got same issue as yours, did you find a solution?

      Delete

Post a Comment

Popular posts from this blog

Android Tip : Handling back button in Fragments

Android Tip : Handling hardware back button in Fragment and DialogFragment This post explains how to handle hardware back button in a Fragment OR DialogFragment . In DialogFragment, it’s quiet straight forward to achieve this. You’ve to get the dialog instance and set onKeyListener on it : if (getDialog() != null ) { getDialog().setOnKeyListener( new DialogInterface.OnKeyListener() { @Override public boolean onKey (DialogInterface dialog, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { Timber.i( "hardware back button pressed" ); } } }); } This can be done in the onViewCreated callback. For fragments, this method doesn’t work and fragments doesn’t have a direct callback to catch this event. So in this case, the approach that we follow is : You

DialogFragment : NullPointerException (support library)

Another weird crash this time ! Here’s the stack trace : Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{<activity.fully.qualified.path>}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Dialog.setContentView(android.view.View)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4524) at android.app.ActivityThread.-wrap19(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1479) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6123) at java.lang.reflect.Method.invoke(Method.java) at com.android