从Backstack添加片段时出现IllegalStateException

时间:2019-07-10 14:57:45

标签: android android-fragments android-dialogfragment illegalstateexception fragment-backstack

我有时会收到以下崩溃报告:

java.lang.IllegalStateException: 
  at androidx.fragment.app.FragmentManagerImpl.addFragment (FragmentManagerImpl.java:1916)
  at androidx.fragment.app.BackStackRecord.executePopOps (BackStackRecord.java:828)
  at androidx.fragment.app.FragmentManagerImpl.executeOps (FragmentManagerImpl.java:2622)
  at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether (FragmentManagerImpl.java:2411)
  at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute (FragmentManagerImpl.java:2366)
  at androidx.fragment.app.FragmentManagerImpl.execPendingActions (FragmentManagerImpl.java:2273)
  at androidx.fragment.app.FragmentManagerImpl$1.run (FragmentManagerImpl.java:733)
  at android.os.Handler.handleCallback (Handler.java:808)
  at android.os.Handler.dispatchMessage (Handler.java:101)
  at android.os.Looper.loop (Looper.java:166)
  at android.app.ActivityThread.main (ActivityThread.java:7529)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:245)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:921)

在FragmentManagerImpl的1916行中,我可以找到:

throw new IllegalStateException("Fragment already added: " + fragment);

因此它说已经添加了一些片段。不幸的是,Google不再在Google Play控制台中显示该消息(已经添加了哪个片段)。据我了解Stacktrace,当从Backstack添加片段时会发生这种异常吗?

我有一个FrameLayout,我在其中添加/删除了片段。我总是用以下代码添加它们:

public void addFragment(FragmentActivity activity, Fragment fragment) {
    FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
    String tag = fragment.getClass().getCanonicalName();
    Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(tag);
    if (fragment.isAdded()) {
        return;
    }
    if (prev != null) {
        transaction.remove(prev);
    }
    transaction.replace(R.id.fragment_container, fragment, tag);
    transaction.addToBackStack(null);
    transaction.commit();

然后使用以下方法添加DialogFragments:

public void openFragmentDialog(FragmentActivity activity, DialogFragment dialogFragment, String tag) {
    FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
    Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(tag);
    if (prev != null) {
        transaction.remove(prev);
    }
    transaction.addToBackStack(null);
    dialogFragment.show(transaction, tag);
}

在哪种情况下会发生IllegalStateException?我是否以错误的方式添加/替换了Fragments / DialogFragments?我永远无法重现该错误。但是我从Android 4.4-Android 9和所有类型的设备上收到报告,我不知道它可能在哪里发生。

是动画还是慢速设备?因为它只是偶尔发生。

3 个答案:

答案 0 :(得分:1)

tag必须是唯一的。如果可能的话,请用null替换,否则请使用唯一的内容。

public void addFragment(FragmentActivity activity, Fragment fragment) {
    FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.fragment_container, fragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

答案 1 :(得分:1)

请注意,您要添加和删除具有相同标签的两个不同片段-标签也应该不同。如果prev片段是另一个类的实例,为什么要使用fragment的类名作为标记来查找它?

您应该添加if语句,以检查prevfragment有时是否不是同一实例(由于相同的标记)。

如果两个片段是同一实例,并且您仍要删除然后添加相同的片段-您可能必须在两个不同的事务中执行此操作,以防止发生"Fragment already added"异常。

答案 2 :(得分:0)

这是我要做的检查和防止重复碎片的步骤:

MyFragment myFragment = (MyFragment) fragmentManager.findFragmentByTag(MY_FRAGMENT_TAG);

if(myFragment != null && myFragment.isVisible()){
    // Return early if the fragment already exists & is visible
    return;

}else if(myFragment == null){
    // Create a new instance of the fragment if none already exist
    myFragment = new MyFragment();
}

// Perform the fragment transaction
fragmentManager.beginTransaction()
    .replace(R.id.content_container, myFragment, MY_FRAGMENT_TAG)
    .addToBackStack(MY_FRAGMENT_TAG) // Pass in the tag if you want to add to the back stack
    .commit();

对于对话框片段,我只是创建一个新实例并在其上调用.show()。该对话框应通过与其互动来关闭:

MyDialog myDialog = new MyDialog();

// I call getChildFragmentManager here b/c I was using this from within a fragment
// but you can get whichever Fragment Manager is appropriate
myDialog.show(getChildFragmentManager(), MY_DIALOG_TAG);