尽管我竭尽全力防止添加多个 fragment ,但我仍然遇到java.lang.IllegalStateException: Fragment already added: VideoFragment

我有一个仅在onCreate上实例化VideoFragment的 Activity 。在试图显示VideoFragment的唯一地方,我首先检查该 fragment 是否已经添加。

private VideoFragment videoFragment;

public void onCreate(Bundle savedInstanceState) {
    ...
    videoFragment = new VideoFragment();
    ...
}

private void showVideoFragment() {
    if (!videoFragment.isAdded()) {
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.replace(R.id.fragment_container, videoFragment, "video").commit();
    }
}

我一直无法始终如一地重现此问题,以便在调试器中进行检查,但是我的运行时错误报告继续为用户报告异常java.lang.IllegalStateException: Fragment already added: VideoFragment,并使用由Android类组成的堆栈跟踪。
/FragmentManager.java:1133→ android.app.FragmentManagerImpl.addFragment
/BackStackRecord.java:648→ android.app.BackStackRecord.run
/FragmentManager.java:1453→ android.app.FragmentManagerImpl.execPendingActions
/FragmentManager.java:443→ android.app.FragmentManagerImpl$1.run
/Handler.java:733→ android.os.Handler.handleCallback
/Handler.java:95→ android.os.Handler.dispatchMessage
/Looper.java:146→ android.os.Looper.loop
/ActivityThread.java:5487→ android.app.ActivityThread.main
/Method.java:-2→ java.lang.reflect.Method.invokeNative
/Method.java:515→ java.lang.reflect.Method.invoke
/ZygoteInit.java:1283→ com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
/ZygoteInit.java:1099→ com.android.internal.os.ZygoteInit.main
/NativeStart.java:-2→ dalvik.system.NativeStart.main
isAdded()中添加的定义是否与检查 fragment 事务使用的定义不匹配?

还是 Activity 中的videoFragment引用有些不同?保存状态http://developer.android.com/guide/components/activities.html#SavingActivityState时,我需要显式处理此问题吗?

还是有一种可靠的替代方法来检查 fragment 是否已添加?

更新

我已经弄清楚了如何半可靠地产生这个问题。
  • 启动应用程序
  • 离开应用程序,并运行其他程序一段时间。在我的Galaxy Nexus(近来这很慢)上,使用Chrome阅读几则新闻似乎就足够了。返回主屏幕时,如果需要花费几秒钟的时间进行渲染,则应用程序可能会抛出 fragment 异常。
  • 重新启动应用程序并触发 fragment 更改

  • 如果我杀死并只是运行该应用程序,一切似乎都很好。或者,如果我离开该应用程序并立即返回,则可以正常工作。只有将应用程序在后台保留一小段时间(足以从内存中删除吗?)时,才会出现 fragment 问题。

    我也尝试了onCreate,但没有任何效果
    View v = findViewById(R.id.fragment_container);
    if(v != null){
        Log.d(TAG, "disabling save for fragment_container");
        v.setSaveEnabled(false);
        v.setSaveFromParentEnabled(false);
    }
    

    我还尝试在运行替换 fragment 事务之前检查Fragment prior = getFragmentManager().findFragmentByTag("video");Fragment prior2 = getFragmentManager().findFragmentById(R.id.fragment_container);,但是它们出现在null上。

    我的问题实际上看起来非常类似于
    https://code.google.com/p/android/issues/detail?id=61247
    尽管时间似乎不如内存/缓存影响那么大。我完全不清楚为什么这个问题已经解决。

    我将尝试生成一个简单的应用程序来复制此问题。我当前使用的是webrtc,而logcat的输出完全被webrtc消息所困扰。

    最佳答案

    我在这里看到一些东西:

  • 当系统重新创建Activity时,可能会出现您的问题。您只需更改设备方向即可simulate它。
  • isAdded()返回false,因为重新创建了Activity,所以该方法被称为VideoFragment的新实例,该实例不了解先前的添加。
  • showVideoFragment()实际上将 fragment 添加到Activity中,而不仅仅是显示它。我建议您将该方法重命名为“addVideoFragment”之类的名称,然后将移至onCreate()方法。 如果这样做,则可以解决问题。
  • 如果您真的想显示或隐藏 fragment ,请使用FragmentTransaction中的方法,例如:
     FragmentManager fm = getFragmentManager();
     fm.beginTransaction()
          .setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
          .show(somefrag) // or hide
          .commit();
    

  • 提示:
    当您先验地知道 fragment 始终是VideoFragment时,您可以简单地使用:
    <fragment
         android:name="com.example.VideoFragment"
         android:id="@+id/video_fragment"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
    

    找到它:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_layout);
        VideoFragment fragment = (VideoFragment) getFragmentManager().findFragmentById(R.id.video_fragmen);
    }
    

    并根据需要进行任何操作。

    10-06 03:01