更换片段时如何防止"java.lang.IllegalStateException:Fragment already added"?

mat*_*ttm 6 java android android-fragments

尽管我努力防止碎片被多次添加,但我还是继续遇到java.lang.IllegalStateException: Fragment already added: VideoFragment.

我有一个活动,其中仅在onCreate中实例化VideoFragment.在我尝试显示VideoFragment的唯一地方,我首先检查是否已添加此片段.

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法在调试器中一致地重现此问题,但我的运行时错误报告继续报告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
Run Code Online (Sandbox Code Playgroud)

添加的定义是否与isAdded()用于检查片段事务的定义不匹配?

或者在某种程度上,活动中的videoFragment引用是不一样的?保存状态http://developer.android.com/guide/components/activities.html#SavingActivityState时,我需要明确处理这个问题吗?

或者是否有可靠的替代方法来检查片段是否已被添加?


UPDATE

我已经想出如何半可靠地产生问题.

  1. 开始申请
  2. 远离应用程序,并运行其他程序一段时间.在我的Galaxy Nexus上(这些天很慢),使用Chrome阅读一些新闻文章似乎已经足够了.返回主屏幕时,如果渲染需要几秒钟,则应用程序可能会抛出片段异常.
  3. 重启应用程序并触发片段更改

如果我杀了并且只是运行应用程序,一切似乎都没问题.或者如果我离开应用程序并立即返回,它可以正常工作.只有当应用程序在后台保留一段时间(足以从内存中删除?)时才会出现片段问题.

我也尝试过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);
}
Run Code Online (Sandbox Code Playgroud)

我还尝试检查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消息.

kli*_*mat 6

我在这看到一些事情:

  1. Activity系统重新创建时,您的问题可能会出现问题.您可以通过更改设备方向来简单地模拟它.
  2. isAdded()返回false,因为Activity被重新创建,因此调用此方法的新instatnce,VideoFragment其中不知道以前的添加.
  3. showVideoFragment()实际上添加片段Activity而不是只显示它.我建议你重命名方法类似somethink"addVideoFragment"和移动onCreate()的方法.如果您这样做,则可以解决问题.
  4. 如果你真的想显示或隐藏片段使用方法,FragmentTransaction例如:

     FragmentManager fm = getFragmentManager();
     fm.beginTransaction()
          .setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
          .show(somefrag) // or hide
          .commit();
    
    Run Code Online (Sandbox Code Playgroud)

提示:
当你先验知道你的片段总是VideoFragment你可以简单地使用:

<fragment 
     android:name="com.example.VideoFragment"
     android:id="@+id/video_fragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />
Run Code Online (Sandbox Code Playgroud)

找到它:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_layout);
    VideoFragment fragment = (VideoFragment) getFragmentManager().findFragmentById(R.id.video_fragmen);
}
Run Code Online (Sandbox Code Playgroud)

并根据实例制作任何你想要的东西.


mat*_*ttm 3

我想我已经成功修复了这个错误,通过尝试在一个更简单的示例中重现这个错误: https ://stackoverflow.com/a/30672516/4107809

onCreate我犯了一个错误,在由 Activity 的重新创建引起的连续调用中添加了片段(不是 VideoFragment)的多个实例。此片段添加不会触发,java.lang.IllegalStateException: Fragment already added因为显然只有当您尝试多次添加同一片段实例时才会发生这种情况,而不是同一片段的多个实例。

调用fragmentreplace方法后,java.lang.IllegalStateException: Fragment already added会为新的VideoFragment生成,即使VideoFragment仅使用replace添加一次。

通过确保仅添加一次不同的片段,VideoFragment 的替换不再生成java.lang.IllegalStateException: Fragment already added: VideoFragment,至少对于我上面概述的再现步骤而言是这样。IllegalStateException 似乎与添加/替换 VideoFragment 无关,但与被替换的片段的状态有关。

我对这个决议感到不满,原因有二:

  1. 该错误消息具有误导性。它说 VideoFragment 已经被添加,我已经通过确保不同的片段不会添加多次来解决这个问题,这不会生成异常。

  2. replace文档非常具有误导性。根据我的阅读,在调用替换之前片段容器的状态是什么并不重要;最终状态应仅由从替换参数添加的片段确定。我认为这种差异在相关问题中最为明显,尽管该问题的回答者不同意。

替换添加到容器中的现有片段。这本质上与对所有当前添加的片段调用remove(Fragment)相同,这些片段是使用相同的containerViewId添加的,然后使用此处给出的相同参数调用add(int, Fragment, String)。