1.当快速双击调用FragmentTransaction.add()方法添加fragmentA,而fragmentA不是每次单独生成的因为DialogFragment.show()内部调用了FragmentTransaction.add()方法,所以调用DialogFragment.show()方法时候也可能会出现这个异常。
开始想在add()方法时候,先判断fragmentA.isAdded(),如下调用可以避免该异常:
if(!fragmentA.isAdded()){
FragmentManager manager = ((FragmentActivity)context).getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.add(fragmentA, "fragment_name");
ft.commit();
}
后来发现,在不断快速切换不同的Fragment的时候,isAdded()偶尔会显示false,就因为isAdded()显示了false,那么 ft.add( R.id.main_content, fragment )就会再次执行一次,就会报错,说明通过isAdded()这个方法判断Fragment是否被add可能并不准确。
最终解决方法:
解决方法就是每次add的时候加上一个tag,然后不仅要通过isAdded()判断Fragment是否add,还要通过FragmentManager.findFragmentByTag(tag)获取Fragment,然后判断此Fragment是否为null。
private void addFragment(FragmentManager fm, Fragment fragment, String tag) {
if (!fragment.isAdded()&&null == fm.findFragmentByTag( tag )) {
FragmentTransaction ft = fm.beginTransaction();
fm.executePendingTransactions();
ft.add( R.id.main_content, fragment, tag );
ft.commitAllowingStateLoss();
}
}
2.DialogFragment的show方法引发的该bug
查看DialogFragment的show方法的源码,发现每次show的时候都会提交一个add fragment的事务:
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
所以当快速多次点击按钮时,调用了多次的show方法,添加了多个add事务(add事务A、add事务B等等)。
然后系统在执行事务队列时,在执行了add Fragment后,发现又要add这个fragment,就报异常了,FragmentManager中源码:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
synchronized (mAdded) {
mAdded.add(fragment);
}
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mView == null) {
fragment.mHiddenChanged = false;
}
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
就是快速多次点击按钮时,添加了多个连续的add事务,
而系统在执行add fragment时如果已经add过了当前fragment,则不允许再add,add就报异常。
解决办法:
既然知道异常的原因是【执行了多次show方法,添加了多个连续的add事务】
那我们就改写下show方法,让add事务不连续,每次add之前都把原来的remove掉。
解决代码如下:
public class TestDialogFragment extends DialogFragment {
其他代码......
@Override
public void show(FragmentManager manager, String tag) {
try {
//在每个add事务前增加一个remove事务,防止连续的add
manager.beginTransaction().remove(this).commit();
super.show(manager, tag);
} catch (Exception e) {
//同一实例使用不同的tag会异常,这里捕获一下
e.printStackTrace();
}
}