fragment 基本上是每個(gè)項(xiàng)目都會(huì)用到彤钟,一般我們會(huì)這么寫:
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, new MyFragment())
.commit();
但是有時(shí)候會(huì)報(bào)如下錯(cuò)誤信息:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
意思就是說我們不能在調(diào)用onSaveInstanceState
進(jìn)行commit
操作来候。網(wǎng)上的解決辦法是使用commitAllowingStateLoss
替換commit
。確實(shí)是不報(bào)錯(cuò)了逸雹,但是為什么呢营搅?
來,開始我們的偵探之旅吧0鹪摇(有點(diǎn)繞转质,請(qǐng)一步步往下看)
首先找到FragmentTransaction
類。
public abstract class FragmentTransaction {
public abstract int commit();
public abstract int commitAllowingStateLoss();
}
原來commit
和commitAllowingStateLoss
是抽象方法帖世。繼續(xù)往下找休蟹。
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
}
發(fā)現(xiàn)BackStackRecord
類繼承了FragmentTransaction
。可以看出赂弓,不同之處只有commitInternal
的參數(shù)绑榴。感覺離真相又近了一步,繼續(xù)往下看:
int commitInternal(boolean allowStateLoss) {
// ...不顯示無關(guān)代碼
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
在commitInternal
方法內(nèi)盈魁,只有mManager.enqueueAction(this, allowStateLoss);
使用了該布爾值參數(shù)翔怎。離真相還差一點(diǎn)了,繼續(xù)推測:
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
// ...無關(guān)代碼
}
}
有了突破性進(jìn)展杨耙!此處對(duì)allowStateLoss
值進(jìn)行了判斷赤套。checkStateLoss
按照命名意思是校驗(yàn)狀態(tài)。離真相僅剩一步了珊膜!
private void checkStateLoss() {
if (isStateSaved()) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
@Override
public boolean isStateSaved() {
// See saveAllState() for the explanation of this. We do this for
// all platform versions, to keep our behavior more consistent between
// them.
return mStateSaved || mStopped;
}
這里會(huì)拋出異常信息容握,明顯就是文章開頭碰到的異常錯(cuò)誤信息!isStateSaved()
方法也一同顯示了车柠。到了此時(shí)剔氏,可以明白commit
是會(huì)對(duì)狀態(tài)進(jìn)行檢測,并拋出異常堪遂;而commitAllowingStateLoss
方法只是不進(jìn)行狀態(tài)檢測,因此不會(huì)拋出異常萌庆。這明顯是有點(diǎn)逃避問題溶褪,那么這個(gè)狀態(tài)是什么判斷而得出的呢?
Parcelable saveAllState() {
// ...無關(guān)代碼
mStateSaved = true;
// ...無關(guān)代碼
}
我們主要看mStateSaved
變量践险。在saveAllState
方法內(nèi)猿妈,把mStateSaved
賦值為 true。有意思的是saveAllState
是在Activity
和FragmentActivity
內(nèi)的onSaveInstanceState
方法調(diào)用的巍虫。
總結(jié):
有點(diǎn)繞~最后在此理順整體思路:在Activity
和FragmentActivity
內(nèi)的onSaveInstanceState
方法保存了fragment
的狀態(tài)彭则。在onSaveInstanceState
方法后調(diào)用commit
和commitAllowingStateLoss
會(huì)引起一種問題:因內(nèi)存不足而把不顯示在前臺(tái)的 activity (帶有 fragment)銷毀,之后用戶再回到此 activity 頁面時(shí)占遥,是會(huì)丟失在onSaveInstanceState
后調(diào)用commit
方法提交的頁面狀態(tài)信息俯抖!
不同的只是調(diào)用commit
會(huì)報(bào)錯(cuò),調(diào)用commitAllowingStateLoss
不報(bào)錯(cuò)(睜一只眼閉一只眼)瓦胎。
谷歌在commitAllowingStateLoss
方法的注釋上也寫明芬萍,調(diào)用此方法會(huì)有丟失頁面狀態(tài)信息的風(fēng)險(xiǎn)搔啊。
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.
- 一般情況下柬祠,盡量提早調(diào)用 commit 方法,比如說
onCreate()
负芋; - 異步回調(diào)中避免使用
commit
漫蛔; - 不得已的情況,可以使用
commitAllowingStateLoss
替代commit
。畢竟報(bào)錯(cuò)奔潰莽龟,比頁面狀態(tài)信息丟失更嚴(yán)重蠕嫁;