56

I use this method on my container Activity to show a BFrag

public void showBFrag()
{
    // Start a new FragmentTransaction
    FragmentTransaction fragmentTransaction = mFragmentMgr.beginTransaction();

    if(mBFrag.isAdded())
    {
        Log.d(LOG_TAG, "Show() BFrag");
        fragmentTransaction.show(mBFrag);   
    }
    else
    {
        Log.d(LOG_TAG, "Replacing AFrag -> BFrag");
        fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag);
    }

    // Keep the transaction in the back stack so it will be reversed when backbutton is pressed
    fragmentTransaction.addToBackStack(null);

    // Commit transaction
    fragmentTransaction.commit();        
}

I call it from my container Activity; for the first time:

  • gets into the else statement and mBFrag replace mAFrag.

Then I press the back button:

  • and the operation is reversed (mAFrag is shown but.. does mBFrag is removed?).

Then I go forward again by calling showBFrag() from the same Activity:

  • and it gets AGAIN into the else statement. (so I can deduce that mBFrag is NOT ADDED)
  • but I got a Fragment already added IllegalStateException... (so why it didn't get into the if statement instead?)

So:

  1. Why is the isAdded() method not returning TRUE if I'm getting a Fragment already added IllegalStateException??
  2. Does popBackStack operation completely remove previously added fragments?
  3. What behaviour am I misunderstanding?

EDIT: Here is the complete info of the exception.

06-07 12:08:32.730: ERROR/AndroidRuntime(8576): java.lang.IllegalStateException: Fragment already added: BFrag{40b28158 id=0x7f0c0085}
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.doAddOp(BackStackRecord.java:322)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.replace(BackStackRecord.java:360)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.replace(BackStackRecord.java:352)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at myPackageName.containerActivity.showBFrag() // This line: "fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag);"
Axel M. Garcia
  • 5,138
  • 9
  • 27
  • 29

11 Answers11

44

In the end my workaround was to execute remove() of the previous fragment and add() the new one. Although that's what replace() method was meant to do.

But I am still guessing why replace() method didn't work properly in this case. It is really weird and I want to discard that it is because I am misunderstanding something or doing something wrong.

Shark Lasers
  • 441
  • 6
  • 15
Axel M. Garcia
  • 5,138
  • 9
  • 27
  • 29
  • 1
    @AMGG I would be interested to know whether you tested with the compatibility library and non-compat versions and found this to be a problem with both as I'm using `replace(...)` with the former without problems (with my very first fragment being added with `add(...)`. – PJL Jun 07 '11 at 15:59
  • @AMGG oh nice, I never actually tried calling remove before as it says that is what replace does, glad its working for you, is this in the CPL or 3.0? – Nathan Schwermann Jun 07 '11 at 17:28
  • No, I'm NOT using the CPL. I'm using the Google APIs Platform 3.0 and API Level 11. – Axel M. Garcia Jun 08 '11 at 07:55
  • 11
    @AMGG, @schwiz, this is also a bug in the CPL. The problem is in `BackStackRecord:popFromBackStack` where on an `OP_REPLACE' the activity is assigned to a variable in the fragment that has been removed and not in the one which is being re-added. I'll raise a bug report unless anybody else has the desire to do so. Does someone need to clarify their answer with the fact that it is a workaround to a bug and not necessary a problem with hanging onto reference? Cheers guys. – PJL Jun 08 '11 at 09:06
  • @PJL, @schwiz, thx for all the info. @PJL share the link to the bug report so we can vote it up. Meanwhile my workaround is working fine. Cheers. – Axel M. Garcia Jun 08 '11 at 09:38
  • 7
    @AMGG, @schwiz [bug report link](http://code.google.com/p/android/issues/detail?id=17488). Issue 17488. – PJL Jun 08 '11 at 11:07
  • 1
    Sorry, bug already exists, [link](http://code.google.com/p/android/issues/detail?id=16996). I've voted that one up instead. – PJL Jun 08 '11 at 11:35
  • 2
    Has anyone found a better solution to this problem or is using remove() and then add() the only viable solution right now? – Hank Jun 29 '11 at 20:36
  • 2
    You said remove of the previous fragment, but how do you find this fragment ? With the FragmentManager ? – Tsunaze Jul 01 '11 at 14:38
  • @AMGG Please see my related question: http://stackoverflow.com/questions/24833912/refresh-fragment-ui-from-fragmentactivity – Dr.jacky Jul 19 '14 at 08:08
  • Maybe this is applicable but In my application I bypass the normal async behavior of the fragment by calling getFragmentManager().executePendingTransactions() right after both .remove() and .add(). – Logic1 Dec 26 '15 at 00:29
12

If the state of the activity has already been saved its no longer safe to call commit. You must call commitAllowingStateLoss() instead. Hope this helps!

Edit: ok I've taken a closer look at your issue, problem is you are trying to add a fragment that has already been added. Even if you use replace or remove calls you can't do this. Only work around I have found is to create a new instance of a fragment and add it every time. Once you remove or replace a fragment it is best to drop all of your references to it so the GC can take care of it.

Nathan Schwermann
  • 31,285
  • 16
  • 80
  • 91
  • 1
    There's nothing to indicate that's it's not safe to perform the commit and from experience you would get a different exception thrown anyway if this was the case. As far as I can see the user has simply gone back by pressing the back button and then made a selection somewhere to load up the fragment again, similar say to the FragmentStack API demos example from AndroidDevelopers. I would compare with that example to see where the difference lies. – PJL Jun 06 '11 at 20:49
  • @PJL my experience is an IllegalStateException is thrown in this situation. – Nathan Schwermann Jun 06 '11 at 22:24
  • Sorry yes same exception but meant a differnt message, for example **"Can not perform this action after onSaveInstanceState"**. Again nothing to indicate that it's not save to call `commit`. – PJL Jun 07 '11 at 08:30
  • 1
    commitAllowingStateLoss() didn't solved it. But its good to know about it. Thx – Axel M. Garcia Jun 07 '11 at 13:46
  • 1
    @AMGG ok sorry for the confusion see my updated answer I have ran in this problem as well. Not sure if this is a problem with the compatibility library or with fragments in general. – Nathan Schwermann Jun 07 '11 at 17:26
8

Probably not related to this issue directly, but I've also noticed that setting a transition to the FragmentTransaction will cause an IllegalStateException, while not setting a transition will not.

Here's the bug for this issue: http://code.google.com/p/android/issues/detail?id=25598

Johan Paul
  • 2,203
  • 2
  • 22
  • 38
2

I used this:

if (getFragmentManager().findFragmentByTag(newFragment.getClass().getName()) != null) {
   transaction.remove(newFragment);
 }

and added fragment with

MyFragment frag = new MyFragment(); 
transaction.add(R.id.container, frag, MyFragment.class.getName())

MyFragment.class.getName() stands for tag

Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
Nartus Team
  • 170
  • 1
  • 7
1

Check whether the frament is already added or not using the method fragment.isAdded() Do replace or add the fragment accordingly

Jiju Induchoodan
  • 4,236
  • 1
  • 22
  • 24
1

try this after fragmentTransection.replace()

fragmentTransection.addToBackStack(null);
fragmentTransection.commitAllowingStateLoss();
1

Removing setOffscreenPageLimit from Viewpager solved my issue. Thanks.

Pradeep Kumar Kushwaha
  • 2,231
  • 3
  • 23
  • 34
0

I tried calling FragmentTransaction.remove() from onTabUnselected() and it worked around this bug.

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    ft.add(R.id.fragment_container, fragment, null);
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    ft.remove(fragment);
}
Ahmad
  • 69,608
  • 17
  • 111
  • 137
ObviousChild
  • 119
  • 1
  • 9
0

This code is working fine for me. try this

((MiActivity)getActivity()).addAccount = new AddAccount();
((MiActivity)getActivity()).addAccount.setArguments(params);
fragmentManager = getActivity().getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container((MiActivity)getActivity()).addAccount);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Muhammad Aamir Ali
  • 20,419
  • 10
  • 66
  • 57
0

Solved it by looping through my fragments and checking if isAdded() is true, then removing that fragment. More details here.

Community
  • 1
  • 1
kylarsturn
  • 125
  • 1
  • 8
0

use list keep fragment instance, and judge this:

        if (!mFragmentTags.contains(fragTag + "")) {
                transaction.add(R.id.tab_main_container, mCurrentFragment, fragTag + "");
}

litchi
  • 1