前段时间有大佬反应在使用DialogFragment的时候使用show的时候有出现程序crash的问题,有大神给的解决方案是重写show方法,因为在show方法中实际调用的是FragmentTransaction的commit()方法,将其改用commitAllowingStateLoss就可以了。
其实这个问题到现在也在困扰我,那就是commit()方法和commitAllowingStateLoss方法,他们之间有啥区别?带着这个问题我们来看看FragmentTransaction的源码!
/**
* Schedules a commit of this transaction. The commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.
*
* <p class="note">A transaction can only be committed with this method
* prior to its containing activity saving its state. If the commit is
* attempted after that point, an exception will be thrown. This is
* because the state after the commit can be lost if the activity needs to
* be restored from its state. See {@link #commitAllowingStateLoss()} for
* situations where it may be okay to lose the commit.</p>
*
* @return Returns the identifier of this transaction's back stack entry,
* if {@link #addToBackStack(String)} had been called. Otherwise, returns
* a negative number.
*/
public abstract int commit();
/**
* Like {@link #commit} but allows the commit to be executed after an
* activity's state is saved. This is dangerous because the commit can
* be lost if the activity needs to later be restored from its state, so
* this should only be used for cases where it is okay for the UI state
* to change unexpectedly on the user.
*/
public abstract int commitAllowingStateLoss();
结果发现这货是个抽象类哈,既然是抽象类,那就一定有实现。找到commit()和commitAllowingStateLoss的实现瞅瞅!然后在BackStackState的内部类BackStackRecord中找到了这两货
@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
然后我们就看到了这样的一幕,然来这两货都调用的同一个方法 commitInternal 其区别仅仅是一个true和false,下面我们再去看看这个 commitInternal 里面究竟是怎么处理的这个 true和 false吧!
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);
pw.close();
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
看这个方法名应该是入了个队,具体怎么玩,先不管,进去看看再说!
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}
看来一切的真相就在这个checkStateLoss中了,并且根据前文来看只有在我们调用commit方法时才会进入这个方法。进去看看,反正警察叔叔又不会请我去喝茶!
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调用onSaveInstanceState方法之后是不能执行commit函数的,再下面一个我暂时还没找到mNoTransactionsBecause这个字符串实在什么地方赋值的!看来还得继续研究研究。
那么我们用这commit()和commitAllowingStateLoss()两个函数应该怎么用呢?还是先有道词典一下这两个函数的注释吧!
commit:
安排该事务的提交。这一承诺不会立即发生;它将被安排在主线程上,以便在线程准备好的时候完成。
commitAllowingStateLoss:
与 commit类似,但允许在活动状态保存后执行提交。这是危险的,因为如果Activity需要从其状态恢复,那么提交就会丢失,因此,只有在用户可以意外地更改UI状态的情况下,才可以使用该提交。
万能的Google已经在代码里面告诉我们要如何去用了,具体怎么运用还是得看大家奇巧淫技了!