片段已添加 IllegalStateException

56

我在我的容器Activity上使用这种方法来展示BFrag

public void showBFrag()
{
    // Start a new FragmentTransaction
    FragmentTransaction fragmentTransaction = mFragmentMgr.beginTransaction();

    if(mBFrag.isAdded())
    {
        Log.d(LOG_TAG, "Show() BFrag");
        fragmentTransaction.show(mBFrag);   
    }
    else
    {
        Log.d(LOG_TAG, "Replacing AFrag -> BFrag");
        fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag);
    }

    // Keep the transaction in the back stack so it will be reversed when backbutton is pressed
    fragmentTransaction.addToBackStack(null);

    // Commit transaction
    fragmentTransaction.commit();        
}

我从我的容器 Activity 中调用它;第一次:

  • 进入 else 语句,mBFrag 替换了 mAFrag。

然后我按下返回按钮:

  • 操作被反转(显示 mAFrag 但是... mBFrag 被移除了吗?)。

然后我再次通过从同一个 Activity 调用 showBFrag() 前进:

  • 它再次进入 else 语句。(因此我可以推断出 mBFrag 没有被添加)
  • 但是我得到了 Fragment already added IllegalStateException…(那么为什么它没有进入 if 语句呢?)

所以:

  1. 如果我收到了 Fragment already added IllegalStateException,为什么 isAdded() 方法没有返回 TRUE?
  2. popBackStack 操作完全删除先前添加的片段吗?
  3. 我误解了什么行为?

编辑: 这里是异常的完整信息。

06-07 12:08:32.730: ERROR/AndroidRuntime(8576): java.lang.IllegalStateException: Fragment already added: BFrag{40b28158 id=0x7f0c0085}
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.doAddOp(BackStackRecord.java:322)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.replace(BackStackRecord.java:360)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at android.app.BackStackRecord.replace(BackStackRecord.java:352)
06-07 12:08:32.730: ERROR/AndroidRuntime(8576):     at myPackageName.containerActivity.showBFrag() // This line: "fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag);"

1
你的片段是单例吗? - clauziere
https://twitter.com/cstruter/status/900982307812724736 - cstruter
11个回答

44
最终,我的解决方法是执行前一个片段的remove()并添加新片段的add()。尽管这正是replace()方法的目的。
但我仍然猜测为什么replace()方法在这种情况下无法正常工作。这真的很奇怪,我想排除我是否误解了某些内容或做错了什么。

1
@AMGG 我很想知道你是否测试了兼容库和非兼容版本,并发现两者都存在问题,因为我正在使用前者的replace(...)而没有遇到问题(我的第一个片段是通过add(...)添加的)。 - PJL
11
@AMGG,@schwiz,这也是CPL中的一个错误。问题出在BackStackRecord:popFromBackStack中,在OP_REPLACE时,将活动分配给已删除的片段中的变量,而不是重新添加的片段。除非其他人有意愿这样做,否则我将提出错误报告。有人需要澄清他们的答案是对错误的解决方法,而不是必须保留引用的问题吗?谢谢大家。 - PJL
7
@AMGG,@schwiz错误报告链接。问题编号17488。 - PJL
2
有没有人找到了更好的解决方案,或者现在使用 remove() 然后 add() 是唯一可行的解决方案? - Hank
2
你说要移除上一个片段,但是你怎么找到这个片段?用FragmentManager吗? - Tsunaze
显示剩余6条评论

12
如果活动的状态已经被保存,调用commit将不再安全。您必须改为调用commitAllowingStateLoss()。希望这能帮到您!
编辑:好的,我仔细查看了您的问题,问题在于您正在尝试添加一个已经添加过的片段。即使您使用替换或删除调用也无法做到这一点。我找到的唯一解决方法是每次创建一个新的片段实例并添加它。一旦你删除或替换了一个片段,最好放弃所有对它的引用,这样GC就可以处理它了。

1
没有任何迹象表明执行提交是不安全的,而且从经验上来看,如果是这种情况,您将会得到一个不同的异常抛出。就我所看到的,用户只是通过按下返回按钮然后在某个地方进行选择以再次加载片段,类似于AndroidDevelopers中的FragmentStack API演示示例。我会与该示例进行比较,以查看差异所在。 - PJL
@PJL 我的经验是在这种情况下会抛出 IllegalStateException 异常。 - Nathan Schwermann
抱歉,是相同的异常,但意思不同,例如“在onSaveInstanceState之后无法执行此操作”。再次强调,并没有任何指示表明调用commit是不安全的。 - PJL
1
commitAllowingStateLoss()并没有解决它。但是了解它还是很好的。谢谢。 - Axel M. Garcia
1
@AMGG 对不起,之前有些混淆。请看我的更新答案。我也遇到过这个问题。不确定是兼容性库的问题还是片段本身的问题。 - Nathan Schwermann

8

2
我使用了这个:

if (getFragmentManager().findFragmentByTag(newFragment.getClass().getName()) != null) {
   transaction.remove(newFragment);
 }

并添加了片段

MyFragment frag = new MyFragment(); 
transaction.add(R.id.container, frag, MyFragment.class.getName())

MyFragment.class.getName() 表示 tag


1
使用方法fragment.isAdded()检查片段是否已添加,根据需要替换或添加片段。

1

Viewpager 中删除 setOffscreenPageLimit 解决了我的问题。 谢谢。


1
在fragmentTransection.replace()之后尝试这个。
fragmentTransection.addToBackStack(null);
fragmentTransection.commitAllowingStateLoss();

0

0
使用列表来保存片段实例,并进行判断:
        if (!mFragmentTags.contains(fragTag + "")) {
                transaction.add(R.id.tab_main_container, mCurrentFragment, fragTag + "");
}


0

我尝试在onTabUnselected()中调用FragmentTransaction.remove(),并且它解决了这个bug。

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    ft.add(R.id.fragment_container, fragment, null);
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    ft.remove(fragment);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接