源码分析commitAllowingStateLoss() 和commit()的区别
2016-10-26 11:20
309 查看
之前在使用Fragment的时候偶尔会有这么一个报错,Can not perform this action after onSaveInstanceState,意思为无法再onSaveInstanceState之后执行该操作,这个操作就是指commit(),之前也没怎么在意,后来通过查看源码去了解了一下这个问题,以下是对这个问题的解析及对应解决办法的对比。
Fragment是我们经常用到的东西,常用的操作有添加(add)、移除(remove)、替换(replace)等,这些操作组成一个集合transaction,我们在通过调用getSupportFragmentManager().beginTransaction()来获取这个FragmentTransaction类的实例来管理这些操作,将他们存进由activity管理的back stack中,这样我们就可以进行fragment变化的回退操作,也可以这样去获取FragmentTransaction类的实例:
首先从我们获取FragmentTransaction类的实例开始,即getSupportFragmentManager(),源码是这样的:
终于找到了我们有用的东西了,这里省略了其他不必要的代码,只留下我们需要用的核心代码,有兴趣可以自己去查看源码,这里还有省略掉我们常用的add、remove、replace等方法,回归正题,看我们要找的两个方法commit()和commitAllowingStateLoss(),他们都调用了commitInternal(boolean allowStateLoss)这个方法,只是传入的参数不同,我们去看commitInternal方法,该方法首先去判断是否已经commit,这个简单,直接跳过,最后他执行的是FragmentTransactionImpl类的enqueueAction方法,好,不要嫌麻烦,我们继续去追踪这个方法:
通过这个方法我们可以看到,他首先去根据commit和commitAllowingStateLoss这两个方法传入的参数不同去判断,然后将任务扔进activity的线程队列中,这里我们重点去看的是checkStateLoss()这个方法:
not perform this action after onSaveInstanceState"异常,这就是我们遇到的问题了,而用commitAllowingStateLoss方法则不会这样,这就与我们之前分析的activity去保存状态对应上了,在activity保存状态完成之后调用commit时,这个mStateSaved就是已经保存状态,所以会抛出异常。
长篇大论终于讲完了,其实回头一看并没有多么复杂,就跟着源码一步一步去找,就会找到我们发生错误的地方,看了源码之后发现,原来并没有多么难,so easy!哈哈
Fragment是我们经常用到的东西,常用的操作有添加(add)、移除(remove)、替换(replace)等,这些操作组成一个集合transaction,我们在通过调用getSupportFragmentManager().beginTransaction()来获取这个FragmentTransaction类的实例来管理这些操作,将他们存进由activity管理的back stack中,这样我们就可以进行fragment变化的回退操作,也可以这样去获取FragmentTransaction类的实例:
FragmentManager mFragmentManager = getSupportFragmentManager(); FragmentTransaction mFragmentTransaction = mFragmentManager.beginTransaction();为什么我们会有这种报错呢,因为我们在使用add(),remove(),replace()等方法将Fragment的变化添加进去,然后在通过commit去提交这些变化(另外,在commit之前可以去调用addToBackState()方法,将这些变化加入到activity管理的back stack中去,这样用户调用返回键就可以回退这些变化了),提交完成之后这些变化就会应用到我们的Fragment中去。但是,这个commit()方法,你只能在avtivity存储他的状态之前调用,也就是onSaveInstanceState(),我们都知道activity有一个保存状态的方法和恢复状态的方法,这个就不详细解释了,在onSaveInstanceState()方法之后去调用commit(),就会抛出我们遇到的这个异常,这是因为在onSaveInstanceState()之后调用commit()方法,这些变化就不会被activity存储,即这些状态会被丢失,但我们可以去用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 { ... ... @Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); } .... ... }我们看到这个实现类中的该方法是返回一个BackStateRecord类的实体,我们继续去追踪这个类,就会发现,这个类其实是继承自FragmentTransaction的,并且,我们在这里看到我们熟悉的方法:
final class BackStackRecord extends FragmentTransaction implements FragmentManager.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) { Log.v(TAG, "Commit: " + this); 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; } }
终于找到了我们有用的东西了,这里省略了其他不必要的代码,只留下我们需要用的核心代码,有兴趣可以自己去查看源码,这里还有省略掉我们常用的add、remove、replace等方法,回归正题,看我们要找的两个方法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<Runnable>(); } 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就是已经保存状态,所以会抛出异常。
长篇大论终于讲完了,其实回头一看并没有多么复杂,就跟着源码一步一步去找,就会找到我们发生错误的地方,看了源码之后发现,原来并没有多么难,so easy!哈哈
相关文章推荐
- FragmentTransaction的commit和commitAllowingStateLoss的区别
- 转: Hibernate commit() 和flush() 的区别
- git commit -m 与 git commit -am 的区别
- 关于COMMIT WORK 和COMMIT WORK AND WAIT的区别及ROLLBACK WORK
- Git------Commit和Push的区别
- 源码分析commitAllowingStateLoss() 和commit()的区别
- git commit -m 与 git commit -am 的区别
- Oracle 临时表(有无on commit preserve rows区别)
- session.flush与transaction.commit 区别
- Mysql 中不commit也可以改变数据库吧,那么写不写commit命令有什么区别呢?
- SharedPrefrence 的 apply() 和 commit() 区别
- FragmentTransaction的commit和commitAllowingStateLoss的区别
- TortoiseSVN中SVN update、SVN commit、add的区别是什么?
- (ora-01410无效的rowid)临时表 on commit delete rows 与 on commit preserve rows 的区别
- Hibernate commit() 和flush() 的区别
- git下commit和push的区别
- SharedPreferences.Editor 的apply()与commit()方法的区别
- git commit -m 与 git commit -am 的区别
- SharedPreference.Editor的apply和commit方法的区别
- SharedPreferences.Editor 的apply()与commit()方法的区别?