情景说明
Android开发中,若是存在多个Fragment,常常能遇到以下Fragment异常,意味着该fragment 被重复add。java
java.lang.IllegalStateException: Fragment already added:xxxFragmentandroid
代码以下缓存
public Fragment showFragment(int position, Bundle bundle) {
try {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
String fragmentTag = createFragmentName(mViewContainer.getId(), position);
Fragment fragment = mFragmentManager.findFragmentByTag(fragmentTag);
if (fragment == null) {
fragment = instantiateItem(position);
fragment.setUserVisibleHint(false);
}
if (mCurrentPrimaryItem != fragment) { //防止重复add
if (mCurrentPrimaryItem != null) {
mCurTransaction.hide(mCurrentPrimaryItem);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment.isAdded()) {
mCurTransaction.show(fragment);
} else{
mCurTransaction.add(mViewContainer.getId(), fragment, fragmentTag);
}
mCurrentPrimaryItem = fragment;
}
if(bundle!=null)
setArgs(bundle);
if (!mCurrentPrimaryItem.getUserVisibleHint()) {
mCurrentPrimaryItem.setUserVisibleHint(true);
}
mCurTransaction.commit();
return fragment;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
思路分析
解决问题的前提每每是分析问题,那么这种问题是如何致使的呢?app
在FragmentTransaction中,咱们经常使用的是add和attach方法来添加fragment,这2个方法中的动做并不会当即执行,而是将OP任务加入了本身的队列。OP任务在等待commit系列方法提交事务以后执行,但同时commit 方法提交的任务加入到了主线程Looper中,若是Looper阻塞,add OP可能会延迟。ide
所以,这种状况下致使屡次点击tab切换不相邻的fragment的时候if (mCurrentPrimaryItem != fragment) 条件可能成立,那么接下来就会调用isAdd()方法 ,但isAdd()方法可能返回的是false ,由于只有Fragment被真正add以后才返回true,但同时findFragmentByTag却能返回当前的fragment示例。oop
解决方法
方法一,commitNow系列
新版FragmentTransaction提供了commitNow方法,这个方法被调用以后,任务不会被加入主线Looper,能够当即执行spa
使用场景:解决数量较小(数量在4之内)和UI和Work相对简单Fragment的add问题,若是是复杂Fragment或者数量较多的Fragment被add,有可能致使卡顿、ANR问题.线程
mCurTransaction.commitNow();
此外,commitNow没法与addToBackStack并用,由于该方法内部使用了disallowAddToBackStack(),若是调用addToBackStack()会发生异常code
方法2、使用Looper队列
commit把OP ADD任务加入到Looper队列中,而且是MainLooper队列,因为队列是先进先出的关系,所以,咱们在OP ADD为完成以前,进行拦截。完成以后删除缓存队列
final Map pendingRequestManagerFragments =
new HashMap<>();
private BaseFragment getFragment(@NonNull FragmentManager fm,String FragmentTAG) {
BaseFragment current = (BaseFragment) fm.findFragmentByTag(FragmentTAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm); //若是存在,则表示OP ADD未完成
if (current == null) {
current = new MyBaseFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
private final Handler handler = new Handler(Looper.getMainLooper(),new Callback(){
@Override
public boolean handleMessage(Message message) {
boolean handled = true;
Object removed = null;
Object key = null;
switch (message.what) {
case ID_REMOVE_FRAGMENT_MANAGER:
android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
key = fm;
removed = pendingRequestManagerFragments.remove(fm);
break;
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
FragmentManager supportFm = (FragmentManager) message.obj;
key = supportFm;
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
default:
handled = false;
break;
}
if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key);
}
return handled;
}
});
使用场景:很是适合数量多,功能复杂的Fragment的事务操做
方法3、改造流程控制方式
fragment在FragmentTransaction的add(fragment) 方法被调用以后,会当即赋值fragment.mFragmentManager赋值为当前的FragmentManager,所以改造方式能够以下:
if (fragment.isAdded()) {
mCurTransaction.show(fragment);
} else if(fragment.getFragmentManager()!=null){
//防止fragment被屡次加载
mCurTransaction.show(fragment);
}else{
mCurTransaction.add(mViewContainer.getId(), fragment, fragmentTag);
}
使用场景:比较适合fragment数量为2个的fragment切换
可是这种方式也存在一个问题,若是涉及频繁的show与hide的切换不一样步问题,在MinLooper中OP hide以后可能OP show了。
2094

被折叠的 条评论
为什么被折叠?



