IllegalStateException: Fragment already added - during app startup on Android OS 4.3

JavaJoe picture JavaJoe · Dec 16, 2013 · Viewed 10.2k times · Source

My Android app has a single activity with a tab controller hosting 4 tabs – each tab is represented by a Fragment. When I run the app on my test device running OS 4.03, the app works and I can navigate to different tabs, etc. When the app is run on a device with OS 4.3, the app crashes during startup with an IllegalStateException: Fragment already added: MyFirstTabFragment.

Here's the stack trace:

java.lang.IllegalStateException: Fragment already added: MyFirstTabFragment{41866dc0 #0 id=0x7f080000 MyFirstTabFragment} at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1128) at android.app.BackStackRecord.run(BackStackRecord.java:616) at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435) at android.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:474) at android.support.v13.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:167) at android.support.v4.view.ViewPager.populate(ViewPager.java:1068) at android.support.v4.view.ViewPager.populate(ViewPager.java:914) at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1436) at android.view.View.measure(View.java:15848) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at android.view.View.measure(View.java:15848) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) at com.android.internal.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:302) at android.view.View.measure(View.java:15848) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2189) at android.view.View.measure(View.java:15848) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1905) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1104) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1284) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1004) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5481) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749) at android.view.Choreographer.doCallbacks(Choreographer.java:562) at android.view.Choreographer.doFrame(Choreographer.java:532) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5103) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method)

The problem appears to be caused by attempting to add the fragments within a FragmentTransaction within the getItem() method of my FragmentStatePagerAdapter extended class as follows:

public class MyFragmentStatePagerAdapter extends android.support.v13.app.FragmentStatePagerAdapter {        

    @Override
    public Fragment getItem(int position) {
        if (position == 0) {
            Fragment fragment = new MyFirstTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MyFirstTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MyFirstTabFragmentTag").commit();

            return fragment;  
        } else if (position == 1) {
            Fragment fragment = new MySecondTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MySecondTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MySecondTabFragmentTag").commit();

            return fragment;  
        }
    }
.
.
.

}

The reason I am calling add() is so that I can associate a tag with my fragments in order to access those fragments later to invoke a method on those fragment instances as tabs are selected/unselected.

I have searched through various posts on the IllegalStateException and have tried various suggestions including calling replace() instead of add(), calling remove() then add(), calling MyFirstTabFragment.instantiate() instead of new MyFirstTabFragment() and none of these changes have corrected the problem.

I suspect that I may be doing this FragmentTransaction too early in the process since the ViewPager is currently in the process of adding the fragments when I attempt to add or replace the fragment with my tag. Does anyone have any better insights to this process? Is there a better place I can make these separate add() calls in order to tag my fragments?

Thanks in advance for any suggestions!

Answer

JavaJoe picture JavaJoe · Dec 17, 2013

So just to complete this thread...

I removed the add() or replace() call in my adapter's getItem() method and simply instantiate the new fragments to avoid the IllegalStateException, but was unable to determine a logical place to move the replace() call so that I could tag the fragments. This is unfortunate as using the tag would be the most reliable solution.

I tried accessing the fragments using the internal "android:switcher..." tag, but that doesn't seem to work - looking at the tag of my fragments, the tag is always null - so maybe they changed the internal behavior to no longer generate tags by default.

I have resorted to maintaining my own collection of fragment instances that I populate during the getItem() call and update during the adapter's destroyItem() call. I hope there are no surprises along the way here that will cause my collection to have instances that are different than what the ViewPager/adapter are storing.