【问题标题】:How can I prevent "java.lang.IllegalStateException: Fragment already added" when replacing fragments?替换片段时如何防止“java.lang.IllegalStateException:已添加片段”?
【发布时间】:2015-06-03 18:04:37
【问题描述】:

尽管我努力防止多次添加片段,但我仍然遇到java.lang.IllegalStateException: Fragment already added: VideoFragment

我有一个活动,其中 VideoFragment 仅在 onCreate 中实例化。在我尝试显示 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. 重新启动应用程序并触发片段更改

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

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

【问题讨论】:

  • 您是否有可靠地重现此错误的特定配方?换句话说,用户采取了哪些步骤导致崩溃?
  • 缺少你的 logcat 的其余部分
  • 为什么要在 showVideoFragment() 上需要它时实例化片段 onCreate?一项检查可能是if (videoFragment == null) { },然后实例化和事务。我的第二张支票是findFragmentByTag("video") == null
  • 我的应用也有类似的问题,堆栈跟踪只提供 Android API 调用,而没有引用我自己的代码。我花了一段时间才想出一个方法,用我从用户消息中得到的小提示来重现错误。就我而言,它发生在设备入睡并在我的应用程序在特定屏幕上运行时再次唤醒时。也许您的崩溃有类似的原因?

标签: java android android-fragments


【解决方案1】:

我在这里看到了一些东西:

  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();
    

提示:
当您先验知道您的片段始终是 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);
}

然后用实例做任何你想做的事。

【讨论】:

  • 我对你的回答有一个困难。此活动的默认片段不是 VideoFragment。 VideoFragment 在用户执行操作后替换界面片段。我无法在 onCreate 中调用当前的 showVideoFragment 方法。我一直在使用 replace() 来维护只有一个片段的堆栈,因为我没有使用后堆栈。对 onCreate 中的所有片段使用 add(),然后使用 show() 在它们之间切换也可以吗?
  • @mattm 我不知道为什么你不能在 onCreate 中调用它,但如果这是真的,你所要做的就是将该片段存储在不同的变量中,例如在 Activity 之外。在某些静态布尔字段或 onSaveInstanceState 中并在 onRestoreInstanceState 中恢复。
  • 在我用来重现此错误的三个步骤中,showVideoFragment 仅在第 3 步(重新启动应用程序并触发片段更改)中调用一次。在第一次调用时触发了非法状态异常。
  • @mattm 粘贴 IllegalStateException 的原因。
  • IllegalStateException 的原因是我在上面发布的:Android 代码中的堆栈跟踪和java.lang.IllegalStateException: Fragment already added: VideoFragment
【解决方案2】:

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

我犯了一个错误,在连续调用 onCreate 时添加了片段的多个实例(不是 VideoFragment),这是由 Activity 的重新创建引起的。这个片段添加没有触发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 文档非常具有误导性。根据我的阅读,在调用替换之前片段容器的状态应该无关紧要;最终状态应仅由从替换参数添加的片段确定。我认为这种差异在相关问题中最为明显,尽管该问题的回答者不同意。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 1970-01-01
    • 2020-11-16
    • 1970-01-01
    相关资源
    最近更新 更多