选择正确的 Fragment #commitXXX() 函数
Fragment commit 有如下4个函数可以选择:
- commit()
public abstract int commit();
- commitAllowingStateLoss()
public abstract int commitAllowingStateLoss();
- commitNow()
public abstract void commitNow();
- commitNowAllowingStateLoss()
public abstract void commitNowAllowingStateLoss();
但有时我们可能会碰到一个异常,信息如下:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
大意是在 activity 的 onSaveInstanceState
调用后再 commit 的 Transaction 导致的异常。为了不抛出异常有人建议使用 commitAllowingStateLoss
来代替 commit
。
public int commit() {
return commitInternal(false);
}
public int commitAllowingStateLoss() {
return commitInternal(true);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
throw new IllegalStateException("commit already called");
}
......
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
// 如果是 false,就需要做状态检查
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
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);
}
}
如果 activity 的状态被保存了,这里再提交就会检查这个状态,符合这个条件就抛出一个异常来终止应用进程。也就是说在 activity 调用了 onSaveInstanceState()
之后,再 commit
一个事务就会出现该异常。那如果不想抛出异常,也可以很简单调用 commitAllowingStateLoss()
方法来略过这个检查就可以了,但是这是危险的,如果 activity 随后需要从它保存的状态中恢复,这个 commit
是会丢失的。因此它仅仅适用在 ui 状态的改变对用户来说是可以接受的,允许丢失一部分状态。
总结:
-
在 Activity 的生命周期方法中提交事务要小心,越早越好,比如
onCreate
。尽量避免在onActivityResult()
方法中提交。 -
避免在异步的回调方法中执行
commit
,因为他们感知不到当前 Activity 生命周期的状态。 -
使用
commitAllowingStateLoss()
代替commit()
。相比于异常 crash,UI 状态的改变对用户来说是可以接受的。 -
如果你需要在 Activity 执行完 onSaveInstanceState() 之后还要进行提交,而且不关心恢复时是否会丢失此次提交,那么可以使用
commitAllowingStateLoss()
或commitNowAllowingStateLoss()
。
官方更推荐使用 commitNow
()和 commitNowAllowingStateLoss()
来代替先执行 commit()/commitAllowingStateLoss()