简体   繁体   English

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

[英]How can I prevent “java.lang.IllegalStateException: Fragment already added” when replacing fragments?

Despite my efforts to prevent fragments from being added more than once, I continue to encounter java.lang.IllegalStateException: Fragment already added: VideoFragment . 尽管我努力防止片段被多次添加,但我仍然遇到java.lang.IllegalStateException: Fragment already added: VideoFragment

I have an activity where VideoFragment is instantiated in onCreate only. 我有一个活动,其中仅在onCreate中实例化VideoFragment。 In the only place I attempt to display the VideoFragment, I first check whether this fragment has been added already. 在我尝试显示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();
    }
}

I have not been able to consistently reproduce this problem to examine in the debugger, but my runtime error reporting continues to report the exception java.lang.IllegalStateException: Fragment already added: VideoFragment for users, with stack traces composed of Android classes. 我无法在调试器中一致地重现此问题,但我的运行时错误报告继续报告异常java.lang.IllegalStateException: Fragment already added: VideoFragment用户的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

Does the definition of added in isAdded() not match the one used to check fragment transactions? 添加在isAdded()的定义是否与用于检查片段事务的定义不匹配?

Or is there some way the videoFragment reference in the activity is not the same? 或者在某种程度上,活动中的videoFragment引用是不一样的? Is this something I need to explicitly handle when saving state http://developer.android.com/guide/components/activities.html#SavingActivityState ? 保存状态http://developer.android.com/guide/components/activities.html#SavingActivityState时,我需要明确处理这个问题吗?

Or is there a reliable alternative way of checking whether the fragment has already been added? 或者是否有可靠的替代方法来检查片段是否已被添加?


UPDATE UPDATE

I have figured out how to semi-reliably produce the problem. 我已经想出如何半可靠地产生问题。

  1. Start application 开始申请
  2. Navigate away from application, and run other programs for awhile. 远离应用程序,并运行其他程序一段时间。 On my Galaxy Nexus (which is pretty slow these days), using Chrome to read a few news articles seems sufficient. 在我的Galaxy Nexus上(这些天很慢),使用Chrome阅读一些新闻文章似乎已经足够了。 When returning to the home screen, if it takes a few seconds to render then the application will likely throw the fragment exception. 返回主屏幕时,如果渲染需要几秒钟,则应用程序可能会抛出片段异常。
  3. Restart application and trigger fragment change 重启应用程序并触发片段更改

If I kill and simply run the application, everything appears to be fine. 如果我杀了并且只是运行应用程序,一切似乎都没问题。 Or if I navigate away from the application and come back immediately, it works okay. 或者如果我离开应用程序并立即返回,它可以正常工作。 It's only if the application is left in the background for a bit (enough to remove from memory?), that the fragment issue appears. 只有当应用程序在后台保留一段时间(足以从内存中删除?)时,才会出现片段问题。

I also tried, to no effect, in onCreate 我也尝试过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);
}

I also tried checking Fragment prior = getFragmentManager().findFragmentByTag("video"); 我也尝试过检查Fragment prior = getFragmentManager().findFragmentByTag("video"); and Fragment prior2 = getFragmentManager().findFragmentById(R.id.fragment_container); Fragment prior2 = getFragmentManager().findFragmentById(R.id.fragment_container); before running the replace fragment transaction, but these come up null . 在运行替换片段事务之前,这些都是null

My problem in fact looks very similar to https://code.google.com/p/android/issues/detail?id=61247 though the time appears less an issue than memory/cache effects. 事实上我的问题看起来与https://code.google.com/p/android/issues/detail?id=61247非常相似,尽管时间似乎不是内存/缓存效果的问题。 It is completely unclear to me why that issue was closed. 我完全不清楚为什么这个问题被关闭了。

I will try to produce a simple application that replicates this issue. 我将尝试生成一个复制此问题的简单应用程序。 My current one uses webrtc, and the logcat output is completely cluttered with webrtc messages. 我当前使用webrtc,logcat输出完全混乱了webrtc消息。

I see a few things here: 我在这看到一些事情:

  1. Your problem occours propably when Activity is recreated by the system. 当系统重新创建Activity时,您的问题可能会出现。 You can simply simulate it by changing device orientation. 您可以通过更改设备方向来简单地模拟它。
  2. isAdded() returns false , because Activity was recreated so this method is called for new instatnce of VideoFragment which hasn't knowledge about previous add. isAdded()返回false ,因为重新创建了Activity ,因此调用此方法用于VideoFragment新安装,该操作不了解先前的添加。
  3. showVideoFragment() actually add fragment to Activity instead of just show it. showVideoFragment()实际上将片段添加到Activity而不是仅显示它。 I suggest you rename that method to somethink like "addVideoFragment" and move it to onCreate() method. 我建议你将这个方法重命名为“addVideoFragment”,并将其移动onCreate()方法。 If you do that you resolve the issue. 如果您这样做,则可以解决问题。
  4. If you really want to show or hide fragment use methods from FragmentTransaction eg: 如果你真的想要显示或隐藏FragmentTransaction片段使用方法,例如:

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

Hint: 暗示:
When you a priori know that you fragment is always the VideoFragment you can simply use: 当您事先知道您的片段始终是VideoFragment您可以简单地使用:

<fragment 
     android:name="com.example.VideoFragment"
     android:id="@+id/video_fragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />

find it: 找到它:

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

and make whatever you want with the instance. 并根据实例制作任何你想要的东西。

I think I have successfully fixed this error, by trying to reproduce this in a simpler example: https://stackoverflow.com/a/30672516/4107809 我想我已经成功修复了这个错误,试图通过一个更简单的例子重现这个: https//stackoverflow.com/a/30672516/4107809

I was making a mistake where multiple instances of a fragment (not the VideoFragment) were added in successive calls to onCreate caused by recreation of the Activity. 我犯了一个错误,因为在重新创建活动时对onCreate连续调用中添加了多个片段实例(不是VideoFragment)。 This fragment addition did not trigger the java.lang.IllegalStateException: Fragment already added because apparently this happens only if you try to add the same fragment instance more than once, not multiple instances of the same fragment. 这个片段添加没有触发java.lang.IllegalStateException: Fragment already added因为显然只有当你尝试多次添加相同的片段实例而不是同一片段的多个实例时才会发生这种情况。

Upon calling the fragment replace method, the java.lang.IllegalStateException: Fragment already added is generated for the new VideoFragment, even though the VideoFragment is only added once using replace. 在调用片段replace方法时,为新的VideoFragment生成java.lang.IllegalStateException: Fragment already addedjava.lang.IllegalStateException: Fragment already added ,即使VideoFragment仅使用replace添加一次。

By ensuring the different fragment was added only once, the replace by the VideoFragment no longer generates java.lang.IllegalStateException: Fragment already added: VideoFragment , at least for the steps for reproducing I outlined above. 通过确保仅添加一次不同的片段,VideoFragment的替换不再生成java.lang.IllegalStateException: Fragment already added: VideoFragment ,至少对于上面概述的再现步骤而言。 The IllegalStateException appears to have nothing to do with adding/replacing the VideoFragment, but with the state of the fragments being replaced. IllegalStateException似乎与添加/替换VideoFragment无关,但与更换片段的状态无关。

I am displeased by this resolution for two reasons: 我对此决议感到不满,原因有两个:

  1. The error message is misleading. 错误消息具有误导性。 It says the VideoFragment has already been added, and I have resolved this by making sure that a different fragment is not added more than once, which did not generate an exception. 它说VideoFragment已经添加了,我已经解决了这个问题,确保不会多次添加不同的片段,这不会产生异常。

  2. The replace documentation is very misleading. replace文档非常具有误导性。 Based on my reading, it should not matter what the state of the fragment container is prior to calling to replace; 根据我的阅读,在调用替换之前片段容器的状态应该无关紧要; the end state should be determined solely by the fragment that is added from the replace argument. 结束状态应仅由replace参数中添加的片段决定。 I think this discrepancy is most clear in the linked question, though the answerer in that question disagrees. 我认为这个差异在相关问题中最为明显,尽管该问题的回答者不同意。

Replace an existing fragment that was added to a container. 替换添加到容器的现有片段。 This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here. 这与使用相同的containerViewId添加的所有当前添加的片段调用remove(Fragment),然后使用此处给出的相同参数添加(int,Fragment,String)基本相同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 java.lang.IllegalStateException:片段已添加 - java.lang.IllegalStateException: Fragment already added java.lang.IllegalStateException:已添加片段: - java.lang.IllegalStateException: Fragment already added : 地图片段。 膨胀布局时:java.lang.IllegalStateException: Fragment already added - MapFragment. When inflating a layout: java.lang.IllegalStateException: Fragment already added java.lang.IllegalStateException:片段已处于活动状态 - java.lang.IllegalStateException: Fragment already active 原因:java.lang.IllegalStateException:从活动中替换任何片段时,onSaveInstanceState之后无法执行此操作 - Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState when replacing any fragment from activity 自定义DialogFragment(扩展BottomSheetDialogFragment),java.lang.IllegalStateException: Fragment already added - Custom DialogFragment (extends BottomSheetDialogFragment), java.lang.IllegalStateException: Fragment already added 片段中的 java.lang.IllegalStateException - java.lang.IllegalStateException in fragment java.lang.IllegalStateException:Fragment - java.lang.IllegalStateException: Fragment 如何防止错误 java.lang.IllegalStateException: Transaction already active, in Hibernate - How to prevent the error java.lang.IllegalStateException: Transaction already active, in Hibernate java.lang.IllegalStateException:即使我正在创建类本身的新实例,Timer也已被取消 - java.lang.IllegalStateException: Timer already cancelled even when i am creating a new instance of the class itself
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM