Fragment already added IllegalStateException - android

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:
Why is the isAdded() method not returning TRUE if I'm getting a Fragment already added IllegalStateException??
Does popBackStack operation completely remove previously added fragments?
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);"

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.

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.

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

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

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

try this after fragmentTransection.replace()
fragmentTransection.addToBackStack(null);
fragmentTransection.commitAllowingStateLoss();

Removing setOffscreenPageLimit from Viewpager solved my issue.
Thanks.

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);
}

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();

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

use list keep fragment instance, and judge this:
if (!mFragmentTags.contains(fragTag + "")) {
transaction.add(R.id.tab_main_container, mCurrentFragment, fragTag + "");
}

Related

Android Fragment setRetainInstance(true) is not working on backstack

Hi I want to keep a Fragment alive even if it is not shown anymore. Because I have some AsyncTasks going there.
Firstly I am adding a starting point Fragment
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fragmentMenu, menuFragment);
fragmentTransaction.commit();
Later on I replace menuFragment with the Fragment which should stay alive
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragmentMenu, btFragment).addToBackStack(null).commit();
Lastly I override the onBackPressed() method for using popBackStack()
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() != 0){
getFragmentManager().popBackStackImmediate();
}else{
super.onBackPressed();
}
}
Inside my btFragment which should stay alive even if I pop another Fragment I have set setRetainInstance(true) inside the onCreate() method.
But it is getting destroyed as soon as I pop the backstack.
Am I doing something wrong? thx
The documentation for setRetainInstance() explains that it applies only to activity recreation:
Control whether a fragment instance is retained across Activity
re-creation (such as from a configuration change)
I solved it with a little bit of a workaround. Here is the code if anyone is interested in it.
// little work-around to not let btFragment die.
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragmentMenu);
if (fragment instanceof BTFragment) {
getFragmentManager().beginTransaction().hide(fragment).add(R.id.fragmentMenu, menuFragment).commit();
} else if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStackImmediate();
} else {
super.onBackPressed();
}
Basically I'm hiding the Fragment instead of removing it and by checking the FrameLayout for the current Fragment I can handle different Fragments. With this way, the onDestroy() method is not called on my btFragment.

executePendingTransactions does not commit FragmentTransaction

I have some simple code, where I am replacing a fragment with another fragment. The problem I have seen is that the new fragment never has any of its create /attach etc lifecycles called.
The code is as follows:
String SERVER="SERVER";
android.support.v4.app.FragmentManager fm = getChildFragmentManager();
FragmentTransaction ft = fm.beginTransaction().setCustomAnimations(R.anim.pop_enter, R.anim.pop_exit );
Fragment s = fm.findFragmentByTag(SERVER);
if(s == null ) s = new ServerFragment();
ft.replace(R.id.fragment_container, s, SERVER);
ft.show(s);
ft.commit();
boolean result = fm.executePendingTransactions();
//Validate if added
Fragment frag = fm.findFragmentByTag(SERVER);
frag.isAdded(); //Returns FALSE!
I would expect the isAdded method to return True and critically I would also expect the usual Fragment lifecycle methods to be invoked. This however is not the case.
Any thoughts would be greatly appreciated?
Regards
not positive here, but it seems like since you're creating a new Fragment each time and not explicitly destroying the old ServerFragments, it's likely that findFragmentByTag is finding the first Fragment created, which would not return true for isAdded() since it's been removed from the view hierarchy.
If that's correct, then you should see the isAdded() call return true the very first time that method is called, and not again after that
I believe there is a function to execute pending transactions that has the word immediate in it. It makes the transaction happen immediately rather than when the program gets to it.

OnRotate crash for DialogFragments

I have a DialogFragment that is shown on the screen from a background thread. When I rotate the screen, my app crashes. Below is the code:
public void showDialog(DialogFragment dialog) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
dialog.show(ft, "dialog");
}
This gives me the following error:
10-24 13:20:51.490: E/AndroidRuntime(3038): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
After looking for answers on SO, and this article, I tried doing this:
public void showDialog(DialogFragment dialog) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
ft.add(dialog, "dialog");
ft.commitAllowingStateLoss();
}
But with this, I get the following error on ft.commitAllowingStateLoss()
10-24 13:26:58.890: E/AndroidRuntime(3765): java.lang.IllegalStateException: Activity has been destroyed
Any idea what am I missing?
You have a method as showDialog(DialogFragment dialog) but your problem is in your dialogFragment's initialization I guess. If you put dialogFragments initialization data to its arguments, your problem will be solved. Write a newInstance method and use it on creation of your dialog. See sample here.
Edit: Reason of your exception is; your activity and also dialogFragment is destroyed and re-created somehow(maybe by config_changes not set at manifest or "settings -> developer options -> don't keep activities" is selected. This can happen, it is normal). And initialization paramaters of your dialog has been lost(became null). You have to put your initialization parameters to dialogFragment's arguments and so it can read them after re-cration on create.
Try this:
FragmentManager fm = getActivity().getSupportFragmentManager();
dialog.setTargetFragment(this, 0);
dialog.show(fm, 0);
Are you creating the fragment using a constructor with parameters?? if so that could be the case because you are not supposed to create fragments that way

findFragmentByTag always returning null on trying to retrieve after add()

I have two fragments, I need to keep them both but show and hide on button clicks.
I added the first fragment using:
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
mDishFragment = new DishFragment();
transaction.add(R.id.dish_fragment, mDishFragment, "DishFragment");
transaction.commit();
First fragment (DishFragment) has a button on clicking of which the code checks if "OrderSummaryFragment" exists(using findFragmentbyTag), if it does, it should show() it else add() a new one. here is the code:
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
if (getFragmentManager().findFragmentByTag("OrderSummaryFragment") == null) {
System.out.println("OrderSummaryFragment not found");
transaction.add(R.id.dish_fragment, mOrderSummaryFragment,"OrderSummaryFragment");
System.out.println("Orderfragment added");
transaction.addToBackStack(null);
transaction.commit();
}else{
System.out.println("OrderSummaryFragment found");
transaction.hide(mDishFragment);
transaction.show(mOrderSummaryFragment);
transaction.commit();
}
For the first time since "OrderSummaryFragment" doesn't exist, the code adds one and it is displayed. There is a back button on the "OrderSummaryFragment" pressing of which show() up the first fragment "DishFragment".
The second time, since we have already added the "Ordersummaryfragment" previously, the findFragmentByTag should return OrderSummaryFragment but it returns null instead.
Note: I am not using replace() cause I want to reuse both of these fragments.
Hope someone can help me out.
Your fragment should search for your fragments using the getSupportFragmentManager() instead of the getFragmentManager Method() in your if structure.
BTW, What is that R.id.dish_fragment object? A fragment? A container? It should be a container like a LinearLayout.

Android Fragment - move from one View to another?

Can i first add a Fragment to a View, then "detach" it, and then "re-attach" it to another View?
In code, i want to:
fragOne one = new fragOne();
getSupportFragmentManager().beginTransaction()
.add(R.id.left, one, "tag").commit();
getSupportFragmentManager().beginTransaction()
.detach(one).commit(); // or .remove(), or .addToBackStack(null).remove()
getSupportFragmentManager().executePendingTransactions();
getSupportFragmentManager().beginTransaction()
.add(R.id.right, one).commit();
But it throws error:
04-05 13:28:03.492: E/AndroidRuntime(7195): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.trybackstack/com.example.trybackstack.MainActivity}: java.lang.IllegalStateException: Can't change container ID of fragment fragOne{40523130 #0 id=0x7f080000 tag}: was 2131230720 now 2131230721
Thanks for help!
I had the same problem but I found that what I really needed was to reparent the fragment's view and not the fragment itself, thus avoiding any fragmentManager transaction.
View vv = fragment.getView();
ViewGroup parent = (ViewGroup)vv.getParent();
parent.removeView(vv);
newparent.addView(vv, layoutParams);
after trying all the answers from similar questions, looks like i've found a way to do the trick.
First issue - you really have to commit and execute remove transaction before trying to add fragment to another container. Thanks for that goes to nave's answer
But this doesn't work every time. The second issue is a back stack. It somehow blocks the transaction.
So the complete code, that works for me looks like:
manager.popBackStackImmediate(null, manager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction().remove(detailFragment).commit();
manager.executePendingTransactions();
manager.beginTransaction()
.replace(R.id.content, masterFragment, masterTag)
.add(R.id.detail, detailFragment, activeTag)
.commit();
I guess you would have this figured out by now, but i dont't see any satisfactory answer.
So, I'm posting this for others who may refer to this in the future.
If you want to move a fragment from one view to another you do the following:
android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment1);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.containerToMoveTo, fragment1);
fragmentTransaction.commit();
This way you do not have to duplicate the fragment.
Please check the solution,you need to create the new instance of the same fragment and instantiate it with state of the old fragment if you want to save the state of the old fragment.
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(one);
Fragment newInstance = fetchOldState(one);
ft.add(R.id.right, newInstance);
ft.commit();
//TO fetch the old state
private Fragment fetchOldState(Fragment f)
{
try {
Fragment.SavedState oldState= mFragmentManager.saveFragmentInstanceState(f);
Fragment newInstance = f.getClass().newInstance();
newInstance.setInitialSavedState(oldState);
return newInstance;
}
catch (Exception e) // InstantiationException, IllegalAccessException
{
}
}
I have run into that problem as well. Sometimes moving fragments works, sometimes you have to create a new instance. It seems that moving fragments does not work, if the fragment keeps being in the "isRemoving" state.
Being removed also seems to be prevented by having a fragment in the backstack.
Adding to the Yan. Yurkin's answer. Make sure not to have any transitions applied to the transaction as these seem to delay the fragment from being detached.

Categories

Resources