android用fragment闪退,Android | 玩转fragment,使用commit出现闪退问题解析

在最近的项目过程中,使用了fragment,我们常规的用法如下:getSupportFragmentManager().beginTransaction().add(R.id.fragment_content, new TestFragment()).commit();

这样的用法对于很多童鞋而言都是比较熟悉的,但是在测试的过程中我们有时候会报如下错误信息:Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

从错误信息就是说我们不能在调用onSaveInstanceState进行commit操作。网上的解决办法是使用commitAllowingStateLoss替换commit。试了一下确实能解决这个问题,但是为什么呢?这就是写这篇的主要原因,划重点。

下面通过源码去看这两个方法的区别:

首先从我们获取 FragmentTransaction 类的实例开始,

即getSupportFragmentManager() ,源码如下:/*** Return the FragmentManager for interacting with fragments associated* with this activity.*/public FragmentManager getSupportFragmentManager() {return mFragments;}

而这个返回的 mFragments 是一个 FragmentManagerImpl 类 的实例,他继承自FragmentManager 这个类:final FragmentManagerImpl mFragments = new FragmentManagerImpl();

我们在 FragmentManager 这个类中还看到 beginTransaction() 这个抽象方法,打开他的实现类:final class FragmentManagerImpl extends FragmentManager{......@Overridepublic FragmentTransaction beginTransaction(){return new BackStackRecord(this);}.... ...}

我们看到这个实现类中的该方法是返回一个 BackStateRecord 类的实体,我们继续去追踪这个类,就会发现,这个类其实是继承自 FragmentTransaction 的,并且,我们在这里看到我们熟悉的方法:final class BackStackRecord extends FragmentTransaction implementsFragmentManager.BackStackEntry, Runnable{public BackStackRecord(FragmentManagerImpl manager){mManager = manager;}public int commit(){return commitInternal(false);}public int commitAllowingStateLoss(){return commitInternal(true);}int commitInternal(boolean allowStateLoss){if (mCommitted) throw new IllegalStateException("commit already called");if (FragmentManagerImpl.DEBUG) {LogWriter logw = new LogWriter(TAG);PrintWriter pw = new PrintWriter(logw);dump(" ", null, pw, null);}mCommitted = true;if (mAddToBackStack) {mIndex = mManager.allocBackStackIndex(this);} else {mIndex = -1;}mManager.enqueueAction(this, allowStateLoss);return mIndex;}}

这里省略了其他不必要的代码,只留下我们需要用的核心代码,看我们要找的两个方法 :commit() 和 commitAllowingStateLoss()

他们都调用了:commitInternal(boolean allowStateLoss)

这个方法,只是传入的参数不同,我们去看 commitInternal 方法,该方法首先去判断是否已经 commit ,这个简单,直接跳过,最后他执行的是 FragmentTransactionImpl 类的 enqueueAction 方法,我们继续去追踪这个方法:public void enqueueAction(Runnable action, boolean allowStateLoss){if (!allowStateLoss) {checkStateLoss();}synchronized (this) {if (mActivity == null) {throw new IllegalStateException("Activity has been destroyed");}if (mPendingActions == null) {mPendingActions = new ArrayList();}mPendingActions.add(action);if (mPendingActions.size() == 1) {mActivity.mHandler.removeCallbacks(mExecCommit);mActivity.mHandler.post(mExecCommit);}}}

通过这个方法我们可以看到,他首先去根据:

commit 和 commitAllowingStateLoss

这两个方法传入的参数不同去判断,然后将任务扔进 activity 的线程队列中,这里我们重点去看的是 checkStateLoss() 这个方法:private void checkStateLoss() {if (mStateSaved) {throw new IllegalStateException("Can not perform this action after onSaveInstanceState");}if (mNoTransactionsBecause != null) {throw new IllegalStateException("Can not perform this action inside of " + mNoTransactionsBecause);}}

只有调用 commit 方法才会执行这里,commitAllowingStateLoss 则直接跳过这步,这里我们调用 commit 方法时,系统判断状态(mStateSaved)是否已经保存,如果已经保存,则抛出:Can not perform this action after onSaveInstanceState

异常,这就是我们遇到的问题了,而用 commitAllowingStateLoss 方法则不会这样,这就与我们之前分析的activity去保存状态对应上了,在 activity 保存状态完成之后调用 commit 时,这个 mStateSaved 就是已经保存状态,所以会抛出异常。

到这里就结束啦!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值