@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
从源代码来看,它们调用了同一个方法只是传递的参数不同,那么就是这个参数导致的不同了,继续往下看,
int commitInternal(boolean allowStateLoss) {
.......
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
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);
}
}
- 不同点出现了,在最后倒数第二段代码中,当allowStateLoss为false的时候,会执行checkStateLoss方法,对应commit方法;当allowStateLoss为true的时候,不会执行checkStateLoss方法,而checkStateLoss又干了什么呢???
- 在checkStateLoss中通过mStateSaved字段判断fragment状态是否保存。如果保存就会异常。也就是在执行commit方法时如果状态保存,就异常;什么状态呢???
就是在Activity异常退出时候,执行的onSaveInstanceState方法保存的状态。在onSaveInstanceState方法中会调用FragmentManager中的saveAllState方法保存fragment的状态
总结:官方文档
使用的 commit方法是在Activity的onSaveInstanceState()之后调用的,这样会出错,因为onSaveInstanceState方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存完状态后再给它添加Fragment就会出错。解决办法就是把commit()方法替换成 commitAllowingStateLoss()就行了,其效果是一样的。
什么时候调用onSaveInstanceState()方法呢???
(1)用户主动按下home 键,系统不能确认activity 是否会被销毁,实际上此刻系统也无法预测将来的场景,比如说内存占用,应用运行情况等,所以系统会调用onSaveInstanceState保存activity状态 ;
(2)activity位于前台,按下电源键,直接锁屏;
(3)横竖屏切换;
(4)activity B启动后位于activity A之前,在某个时刻activity A因为系统回收资源的问题要被kill掉,A通过onSaveInstanceState保存状态。
该异常解决
(1)在activity生命周期函数内谨慎使用commit 方法 ,一般情况下如果能在onCreate 中调用,基本不会出现问题,但是如果在onResume onStart 等方法中调用就需要格外注意,比如说FragmetActivity 的onResume 方法 ,在某些场景下onResume 方法被调用之前,可能依然保存着之前的状态导致异常 。
(2)尽可能避免在一些和生命周期函数异步的方法中调用commit,如AsyncTask 等。
(3)实在没法确定调用时机时,可以用commitAllowingStateLoss 代替 commit ,commitAllowingStateLoss 在状态丢失时不会抛出任何异常,但也正因为如此在一些必须确保状态被保存的场合,最好不要使用 commitAllowingStateLoss 方法。