[英]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. 我已经想出如何半可靠地产生问题。
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: 我在这看到一些事情:
Activity
is recreated by the system. 当系统重新创建Activity
时,您的问题可能会出现。 You can simply simulate it by changing device orientation. 您可以通过更改设备方向来简单地模拟它。 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
新安装,该操作不了解先前的添加。 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. 如果您这样做,则可以解决问题。 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 added
的java.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: 我对此决议感到不满,原因有两个:
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已经添加了,我已经解决了这个问题,确保不会多次添加不同的片段,这不会产生异常。
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.