Android重启后fragment多次,Android Fragment重复添加问题解决方法

情景说明

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了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值