I have a Live Android application, and from market i have received following stack trace and i have no idea why its happening as its not happening in application code but its getting caused by some or the other event from the application (assumption)
I am not using Fragments, still there is a reference of FragmentManager. If any body can throw some light on some hidden facts to avoid this type of issue:
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.onKeyDown(Activity.java:1962) at android.view.KeyEvent.dispatch(KeyEvent.java:2482) at android.app.Activity.dispatchKeyEvent(Activity.java:2274) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668) 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.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:1720) at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258) at android.app.Activity.dispatchKeyEvent(Activity.java:2269) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668) at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851) at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824) at android.view.ViewRoot.handleMessage(ViewRoot.java:2011) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:132) at android.app.ActivityThread.main(ActivityThread.java:4025) 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:841) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) at dalvik.system.NativeStart.main(Native Method)
Step 1 : Override onSaveInstanceState state in respective fragment. And remove super method from it. Step 2 : Use fragmentTransaction. commitAllowingStateLoss( );
commitAllowingStateLoss() Like commit() but allows the commit to be executed after an activity's state is saved. abstract void. commitNow() Commits this transaction synchronously.
This is the most stupid bug I have encountered so far. I had a Fragment
application working perfectly for API < 11, and Force Closing
on API > 11.
I really couldn't figure out what they changed inside the Activity
lifecycle in the call to saveInstance
, but I here is how I solved this :
@Override protected void onSaveInstanceState(Bundle outState) { //No call for super(). Bug on API Level > 11. }
I just do not make the call to .super()
and everything works great. I hope this will save you some time.
EDIT: after some more research, this is a known bug in the support package.
If you need to save the instance, and add something to your outState
Bundle
you can use the following :
@Override protected void onSaveInstanceState(Bundle outState) { outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE"); super.onSaveInstanceState(outState); }
EDIT2: this may also occur if you are trying to perform a transaction after your Activity
is gone in background. To avoid this you should use commitAllowingStateLoss()
EDIT3: The above solutions were fixing issues in the early support.v4 libraries from what I can remember. But if you still have issues with this you MUST also read @AlexLockwood 's blog : Fragment Transactions & Activity State Loss
Summary from the blog post (but I strongly recommend you to read it) :
commit()
transactions after onPause()
on pre-Honeycomb, and onStop()
on post-HoneycombActivity
lifecycle methods. Use onCreate()
, onResumeFragments()
and onPostResume()
commitAllowingStateLoss()
only as a last resortLooking in Android source code on what causes this issue gives that flag mStateSaved in FragmentManagerImpl
class (instance available in Activity) has value true. It is set to true when the back stack is saved (saveAllState) on call from Activity#onSaveInstanceState
. Afterwards the calls from ActivityThread don't reset this flag using available reset methods from FragmentManagerImpl#noteStateNotSaved()
and dispatch()
.
The way I see it there are some available fixes, depending on what your app is doing and using:
Before anything else: I would advertise Alex Lockwood article. Then, from what I've done so far:
For fragments and activities that don't need to keep any state information, call commitAllowStateLoss. Taken from documentation:
Allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user`. I guess this is alright to use if the fragment is showing read-only information. Or even if they do show editable info, use the callbacks methods to retain the edited info.
Just after the transaction is commit (you just called commit()
), make a call to FragmentManager.executePendingTransactions()
.
As Ovidiu Latcu mentioned above, don't call super.onSaveInstanceState()
. But this means you will lose the whole state of your activity along with fragments state.
Override onBackPressed
and in there call only finish()
. This should be OK if you application doesn't use Fragments API; as in super.onBackPressed
there is a call to FragmentManager#popBackStackImmediate()
.
If you are using both Fragments API and the state of your activity is important/vital, then you could try to call using reflection API FragmentManagerImpl#noteStateNotSaved()
. But this is a hack, or one could say it's a workaround. I don't like it, but in my case it's quite acceptable since I have a code from a legacy app that uses deprecated code (TabActivity
and implicitly LocalActivityManager
).
Below is the code that uses reflection:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); invokeFragmentManagerNoteStateNotSaved(); } @SuppressWarnings({ "rawtypes", "unchecked" }) private void invokeFragmentManagerNoteStateNotSaved() { /** * For post-Honeycomb devices */ if (Build.VERSION.SDK_INT < 11) { return; } try { Class cls = getClass(); do { cls = cls.getSuperclass(); } while (!"Activity".equals(cls.getSimpleName())); Field fragmentMgrField = cls.getDeclaredField("mFragments"); fragmentMgrField.setAccessible(true); Object fragmentMgr = fragmentMgrField.get(this); cls = fragmentMgr.getClass(); Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {}); noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {}); Log.d("DLOutState", "Successful call for noteStateNotSaved!!!"); } catch (Exception ex) { Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex); } }
Cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With