java.lang.IllegalStateException: Fragment already added 非法状态异常:片段已添加

13

我有一个问题,涉及一个使用目标SDK 4.3编译和运行的Android应用程序。该应用程序有两个活动(Activity),一个MainActivity,也是启动器活动(Launcher Activity),另一个是SecondActivity。这两个活动都使用片段(Fragments)。为了支持旧设备,使用了支持库(support lib)。

在以下情况下,会出现“IllegalStateException: Fragment already added”错误:

1)启动应用程序->显示MainActivity
2)通过意图(Intent)切换到SecondActivity
3)按下主页按钮(Home Button)
4)等待较长时间(测试了24小时)
5)再次按下应用程序图标->异常。如果时间较短,则像预期那样显示SecondActivity。

我已经阅读了很多有关处理Fragment时的IllegalStateExceptions,但是所有指向replace()方法的问题。在堆栈跟踪中,我的自己的代码从未被调用。

片段(Fragment)在活动(Activity)的onCreate()方法内添加:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(..);
    ListFragment listFragment = this.getCaptureListFragment();
    FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction();
    tx.add(R.id.MainFragmentContainer, listFragment, "list_fragment_tag");
    tx.commit(); 
}

private ListFragment getListFragment() {
    ListFragment listFragment = (ListFragment) this.getSupportFragmentManager().findFragmentByTag("list_fragment_tag");
    if (listFragment == null) {
        listFragment = new ListFragment();
    }
    return listFragment;
}


java.lang.RuntimeException: Unable to start activity    ComponentInfo{de.myexample.demo/de.myexample.demo.ui.SecondActivity}: java.lang.IllegalStateException: Fragment already added: ListFragment{42283f58 #0 id=0x7f060094 de.myexample.demo.ui.ListFragment}
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
    at android.app.ActivityThread.access$600(ActivityThread.java:141)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    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)
Caused by: java.lang.IllegalStateException: Fragment already added: ListFragment{42283f58 #0 id=0x7f060094 de.myexample.demo.ui.ListFragment}
    at android.support.v4.app.FragmentManagerImpl.addFragment(SourceFile:1175)
    at android.support.v4.app.BackStackRecord.run(SourceFile:616)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(SourceFile:1460)
    at android.support.v4.app.FragmentActivity.onStart(SourceFile:556)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171)
    at android.app.Activity.performStart(Activity.java:5143)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
    ... 11 more
java.lang.IllegalStateException: Fragment already added: ListFragment{42283f58 #0 id=0x7f060094 de.myexample.demo.ui.ListFragment}
    at android.support.v4.app.FragmentManagerImpl.addFragment(SourceFile:1175)
    at android.support.v4.app.BackStackRecord.run(SourceFile:616)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(SourceFile:1460)
    at android.support.v4.app.FragmentActivity.onStart(SourceFile:556)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171)
    at android.app.Activity.performStart(Activity.java:5143)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
    at android.app.ActivityThread.access$600(ActivityThread.java:141)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    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)

活动的setContentView在哪里? - Raghunandan
尝试使用tx.replace(R.id.MainFragmentContainer, listFragment)代替使用tx.add()方法。阅读此链接以获取有关片段的更多信息:http://developer.android.com/guide/components/fragments.html。 - Karan Nagpal
感谢@Raghunandan。我已经在帖子中添加了缺失的行。 - creaity
@karannagpal:感谢提供链接,但我已经阅读了那个手册。肯定是我做错了什么,但我不确定是什么。我一定会尝试使用replace方法。但我想知道为什么只有在应用程序长时间处于后台时才出现该错误。我知道Android会在此期间清理东西,但Activity的重新创建似乎与正常生命周期不同。这对我来说是有趣的点。 - creaity
@karannagpal 替换肯定没起作用。在这种情况下,我的代码没有被调用。无论应用程序在进入后台之前添加了多少片段都没有关系。 - creaity
4个回答

17

好的,我自己解决了。

我在onPause()中将所有Fragment移除,并将状态存储在一些布尔变量中。根据这些布尔变量,在onResume()中重新添加Fragment。这样,无论Activity在后台停留多长时间,启动都是稳定的。

boolean addList = false;

@Override
protected void onResume() {
    FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction();
    if (this.addList) {
        ListFragment list = this.getListFragment();
        tx.add(R.id.MainFragmentContainer, list, "list_fragment_tag");
    }

    tx.commit();
    super.onResume();

    this.addList = false;   

}

@Override
protected void onPause() {
    this.addList = this.getListFragment().isAdded();
    ...
    if (this.addList) {
        FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction();
        tx.remove(this.getListFragment());
        tx.commit();
    }
    this.getSupportFragmentManager().executePendingTransactions();
    super.onPause();

}

也许这对于有同样问题的人会有帮助


12
为了重现这个问题,可以在“设置”->“开发者选项”中启用“不保留活动”选项。然后暂停并恢复活动。
这样就不必等待24小时 :)

0

片段管理器在退出时保存其状态。因此,您无需再次添加片段。

像这样做:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(..);

    if (savedInstanceState == null) {
        ListFragment listFragment = this.getCaptureListFragment();
        FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction();
        tx.add(R.id.MainFragmentContainer, listFragment, "list_fragment_tag");
        tx.commit(); 
    }
}

谢谢你的回答。它有所帮助,但并不完全。在Android 4.1上可以解决问题。但在4.4 KitKat设备上,活动为空,没有看到片段。但看起来,这种方式比手动删除要好得多。也许,在if...内部我可以在新设备上手动删除片段。我必须进行更多的测试。但它们非常耗时,因为我必须等待至少24小时才能出现问题。 - creaity

-3

不需要为检查片段的添加状态创建新的布尔字段,因为片段中也有一个方法。 只需使用它: myFragment.isAdded()


1
布尔值不是用于存储片段是否已添加的状态。它用于在存储之前存储状态(请参见我的onPause()方法)。我认为您没有理解问题。 - creaity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接