I'm getting user reports from my app in the market, delivering the following exception:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Apparently it has something to do with a FragmentManager, which I don't use. The stacktrace doesn't show any of my own classes, so I have no idea where this exception occurs and how to prevent it.
For the record: I have a tabhost, and in each tab there is a ActivityGroup switching between Activities.
It's October 2017, and Google makes Android Support Library with the new things call Lifecycle component. It provides some new idea for this 'Can not perform this action after onSaveInstanceState' problem.
In short:
Longer version with explain:
why this problem come out?
It's because you are trying to use
FragmentManager
from your activity(which is going to hold your fragment I suppose?) to commit a transaction for you fragment. Usually this would look like you are trying to do some transaction for an up coming fragment, meanwhile the host activity already callsavedInstanceState
method(user may happen to touch the home button so the activity callsonStop()
, in my case it's the reason)Usually this problem shouldn't happen -- we always try to load fragment into activity at the very beginning, like the
onCreate()
method is a perfect place for this. But sometimes this do happen, especially when you can't decide what fragment you will load to that activity, or you are trying to load fragment from anAsyncTask
block(or anything will take a little time). The time, before the fragment transaction really happens, but after the activity'sonCreate()
method, user can do anything. If user press the home button, which triggers the activity'sonSavedInstanceState()
method, there would be acan not perform this action
crash.If anyone want to see deeper in this issue, I suggest them to take a look at this blog post. It looks deep inside the source code layer and explain a lot about it. Also, it gives the reason that you shouldn't use the
commitAllowingStateLoss()
method to workaround this crash(trust me it offers nothing good for your code)How to fix this?
Should I use
commitAllowingStateLoss()
method to load fragment? Nope you shouldn't;Should I override
onSaveInstanceState
method, ignoresuper
method inside it? Nope you shouldn't;Should I use the magical
isFinishing
inside activity, to check if the host activity is at the right moment for fragment transaction? Yeah this looks like the right way to do.Take a look at what Lifecycle component can do.
Basically, Google makes some implementation inside the
AppCompatActivity
class(and several other base class you should use in your project), which makes it a easier to determine current lifecycle state. Take a look back to our problem: why would this problem happen? It's because we do something at the wrong timing. So we try not to do it, and this problem will be gone.I code a little for my own project, here is what I do using
LifeCycle
. I code in Kotlin.As I show above. I will check the lifecycle state of the host activity. With Lifecycle component within support library, this could be more specific. The code
lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
means, if current state is at leastonResume
, not later than it? Which makes sure my method won't be execute during some other life state(likeonStop
).Is it all done?
Of course not. The code I have shown tells some new way to prevent application from crashing. But if it do go to the state of
onStop
, that line of code wont do things and thus show nothing on your screen. When users come back to the application, they will see an empty screen, that's the empty host activity showing no fragments at all. It's bad experience(yeah a little bit better than a crash).So here I wish there could be something nicer: app won't crash if it comes to life state later than
onResume
, the transaction method is life state aware; besides, the activity will try continue to finished that fragment transaction action, after the user come back to our app.I add something more to this method:
I maintain a list inside this
dispatcher
class, to store those fragment don't have chance to finish the transaction action. And when user come back from home screen and found there is still fragment waiting to be launched, it will go to theresume()
method under the@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
annotation. Now I think it should be working like I expected.Short And working Solution :
Follow Simple Steps
Steps
Step 1 : Override
onSaveInstanceState
state in respective fragment. And remove super method from it.Step 2 : Use
fragmentTransaction.commitAllowingStateLoss( );
instead of
fragmentTransaction.commit( );
while fragment operations.If you inherit from
FragmentActivity
, you must call the superclass inonActivityResult()
:If you don't do this and try to show a fragment dialog box in that method, you may get OP's
IllegalStateException
. (To be honest, I don't quite understand why the super call fixes the problem.onActivityResult()
is called beforeonResume()
, so it should still not be allowed to show a fragment dialog box.)I had the exact same problem. It happened because of the destruction of previous activity. when ı backed the previous activity it was destroyed. I put it base activity (WRONG)
I put it into onStart it was RIGHT
I ended up with creating a base fragment and make all fragments in my app extend it
Then when I try to show a fragment I use
showAllowingStateLoss
instead ofshow
like this:
I came up to this solution from this PR: https://github.com/googlesamples/easypermissions/pull/170/files
If you have crash with popBackStack() or popBackStackImmediate() method please try fixt with:
This is worked for me as well.