我怎样才能防止" java.lang.IllegalStateException:片段已添加"更换碎片时?

时间:2015-06-03 18:04:37

标签: 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();
    }
}

我无法在调试器中一致地重现此问题,但我的运行时错误报告继续为用户报告异常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()中添加的定义是否与用于检查片段事务的定义不匹配?

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

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

更新

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

  1. 启动申请
  2. 远离应用程序,并运行其他程序一段时间。在我的Galaxy Nexus上(这些天很慢),使用Chrome阅读一些新闻文章似乎已经足够了。返回主屏幕时,如果渲染需要几秒钟,则应用程序可能会抛出片段异常。
  3. 重新启动应用程序并触发片段更改
  4. 如果我杀了并且只是运行应用程序,一切似乎都没问题。或者如果我离开应用程序并立即返回,它可以正常工作。仅当应用程序在后台保留一段时间(足以从内存中删除?)时,才会出现片段问题。

    我也尝试过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 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消息。

2 个答案:

答案 0 :(得分:6)

我在这里看到一些事情:

  1. 当系统重新创建Activity时,您的问题可能会出现。您只需更改设备方向即可simulate
  2. isAdded()会返回false,因为Activity已重新创建,因此调用此方法的VideoFragment新的不稳定状态,但对先前的添加没有任何了解。
  3. showVideoFragment()实际上将片段添加到Activity而不是仅显示它。我建议你将这种方法重命名为" 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();
    
  5. <强>提示:
     当您先验知道您的片段始终是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);
    }
    

    并根据实例制作任何您想要的内容。

答案 1 :(得分:1)

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

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

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

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

我对此决议感到不满有两个原因:

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

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

  3.   

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