30

My activity invokes the camera with the ACTION_IMAGE_CAPTURE intent. If the camera activity returns succesfully, I set a flag in the onActivityResult callback, and based on the value of the flag I start a fragment in my onResume to add a caption to the image that was captured. This seems to work ok.

I just got a stack trace from the "wild" complaining that I was trying to commit a fragment transaction after onSaveInstanceState has been called. But I'm doing the commit in my onResume method! Why would android complain about this? I do have android:configChanges="orientation|keyboardHidden|keyboard|screenSize" set in my AndroidManifest.xml, so an orientation change should not trigger this....

This occurred on a Samsung Galaxy S3 (SGH-i747) running 4.0.4

Here is the stack:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1325)
    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:532)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.performFragmentTransition(AddPhotosActivity2.java:278)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.switchToCaptionsFragment(AddPhotosActivity2.java:438)
    at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.onResume(AddPhotosActivity2.java:167)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1158)
    at android.app.Activity.performResume(Activity.java:4544)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2448)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2486)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1187)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4514)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
    at dalvik.system.NativeStart.main(Native Method)

Any help or wisdom is appreciated.

PacificSky
  • 3,422
  • 2
  • 25
  • 24
  • 3
    See this [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) about this topic for more information. – Alex Lockwood Aug 20 '13 at 22:59
  • possible duplicate of [getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"](http://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa) – rds Jul 20 '15 at 15:09

7 Answers7

56

I think I know the answer - I'm using the FragmentActivity from v4 compatibility library, and so I need to perform my fragment transactions in onResumeFragments instead of in onResume. Can someone confirm?

PacificSky
  • 3,422
  • 2
  • 25
  • 24
  • 2
    After I made the change to use onResumeFragments instead of onResume to perform my fragment transactions, these errors have gone away - another data point. I have not implemented the empty fragment workaround and have not needed to. I will note that I also update my app to the latest v4 compatibility library periodically, so it's possible Google fixed things on their end. My activities don't restart on configuration changes - I override this in my manifest and handle configuration changes myself. – PacificSky Feb 01 '13 at 19:13
  • I'm not sure but is this the same as `onPostResume()`? – StuStirling Feb 21 '13 at 15:50
  • @DiscoS2 Yes. `onResumeFragments()` is gauranteed to be called after the activity's state has been restored, and therefore so will `onPostResume()` (since `onPostResume()` is always called after `onResumeFragments()`). – Alex Lockwood Aug 20 '13 at 23:00
  • 2
    Google should have documented this way better. I am still waiting for Google to mention this in one of its guides! – Maxim Rahlis May 14 '14 at 09:00
  • 2
    I just hat this problem after displaying an interstitial... After rotation my dialogfragmets start corretly from onResume. – stefan Mar 09 '15 at 18:06
  • It worked for me thanks ...Earlier I was doing Fragment Transactions in my Activity's OnResume, where I was getting this exception – Prashant Aug 08 '17 at 12:28
15

you can use the method commitAllowingStateLoss()

but be aware you can lose the state of your activity as you can see in google's android reference which explain the different between the two in the following way

Like commit() but 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.

from my experience it may cause the addToBackStack method not to work sometimes so you will need to add it manually on the fragment and of course the state won't be saved (textbox text ext.)

Asaf Manassen
  • 3,893
  • 2
  • 21
  • 19
4

this worked for me... found this out on my own... hope it helps you!

1) do NOT have a global "static" FragmentManager / FragmentTransaction.

2) onCreate, ALWAYS initialize the FragmentManager again!

sample below :-

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}
kurayami88
  • 121
  • 1
  • 1
  • What if you simply use getSupportFragmentManager().beginTransaction() instead of declaring a fragment manager? The error occurs even then. So this might not be the solution. – Ali Kazi May 09 '16 at 07:56
  • @kurayami88 Yes, this solution worked for me. I still have a global static `fragmentManager`, but re-initialising the variable in `onCreate` did the job. – Wrichik Basu Jul 25 '20 at 14:46
2

Update I think I have found an explanation and a solution here: http://code.google.com/p/android/issues/detail?id=23096#c4 I implemented the Empty Fragment Workaround posted there and got no more IllegalStateException so far.

I add the invisible state fragment in my activity like this;

@Override
protected void onCreate(final Bundle args) {
    ...
    if (args == null) {
        final FragmentManager fm = this.getSupportFragmentManager();
        final FragmentTransaction ft = fm.beginTransaction();
        final Fragment emptyFragmentWithCallback = new EmptyFragmentWithCallbackOnResume();
        ft.add(emptyFragmentWithCallback, EmptyFragmentWithCallbackOnResume.TAG);
        ft.commit();
    }

The following code is taken from above link:

public class EmptyFragmentWithCallbackOnResume extends Fragment {
OnFragmentAttachedListener mListener = null;

@Override
public void onAttach(SupportActivity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentAttachedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
    }
}

@Override
public void onResume() {
    super.onResume();
    if (mListener != null) {
        mListener.OnFragmentAttached();
    }
}

public interface OnFragmentAttachedListener {
    public void OnFragmentAttached();
}
}

and invoke fragment transactions that would intuitively go into onResume or onResumeFragments into my custom onFragmentAttached method that is invoked by the invisible state fragment. I do not use onResumeFragments at all and do not issue any fragment transactions in onResume.

So, to sum it up. If you are using the support lib and fragments, pretty much forget about onResume, forget about onResumeFragments and implement your own "onResume" based on the above workaround. This is somewhat ridiculous.


I cannot confirm. I have the exact same problem. even though I am issuing fragment transaction in onResumeFragments. This used to work as I posted here: IllegalStateException - Fragment support library .

It seems the error only occurs on 4.0.3 and 4.0.4. However it does neither occur always nor in my Emulator.

I am using support lib rev. 10 and API 16. I call DialogFragment.show in onResumeFragments and continously get this ridiculous exception from some random users. I cannot reproduce it locally.

Community
  • 1
  • 1
phlebas
  • 1,210
  • 2
  • 13
  • 24
  • Hmmm - I haven't seen this issue repro after I started using onResumeFragments. You're calling through to super.onResumeFragments right? – PacificSky Oct 03 '12 at 18:38
  • Yes I call onResumeFragments.This issue obviously only occurs now and then, e.g. on orientation change. I can definitely say that workaround stopped the issue for me. I received the error every hour before. – phlebas Oct 04 '12 at 00:13
  • Hi phiebas i'm not following should i lauch a new fragment from onResumeFragments or not? – Maxrunner Dec 11 '12 at 19:20
  • Hi. my short answer is no, allthough my experience about this issue is based on support lib rev 10 and API 16. I don't know if the behaviour has been fixed lately. I start fragments using the above workaround, so I completely ignore onResumeFragments and use the custom onFragmentAttached instead. This has solved my issues. I have updated my post to hopefully make it more clear – phlebas Dec 12 '12 at 12:51
2

I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:

  1. My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
  2. In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.

What I made is next:

  1. Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
  2. In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
Oleg Novosad
  • 2,261
  • 1
  • 27
  • 28
1

I first develop my app targeting android 2.2 (SDK 8), using the support v4 library, and when I start using it with 4.2 (SDK 17), I had the same troubles with my fragments. But changing my Manifest to android:minSdkVersion="8" android:targetSdkVersion="17", solved my problems. Maybe this hepls you too.

Andre Rocha
  • 988
  • 1
  • 12
  • 21
0

Add this to your Activity:

 @Override
    protected void onSaveInstanceState(Bundle outState) {
        //No call for super(). Bug on API Level > 11.
    }
Alok Singh
  • 640
  • 4
  • 14