今天工做的時候在 bugly 上看到一個奔潰分析中有這麼一個問題: html
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
java
因而就開始 Google,發現這問題由來已久,早在11年的時候就已經有人在提出這個問題。(時至今日仍出現這問題,不得不說 Google 你是能夠的) android
在 Stack Overflow 上的這篇文章中也說了一些奇技淫巧去解決問題,可是回答者也並不理解爲何會這樣,只知道這樣是能夠解決的。而問題出現的緣由其實也能夠猜想出來,就是在 activity 屢次的進行先後臺切換後,致使了 fragment 的 commit 操做發生在 activity 的 onSaveInstanceState 以後。 bash
本着探索一下的精神,開始看源碼。首先看了 commit 的源碼。戳開 commit 後,看到了 FragmentTransaction 這個抽象類。在源碼的最後能夠看到,這裏定義了四個跟 commit 有關的方法。(因爲英文比較渣,單看英文的註釋彷佛有點難以理解四個方法之間的差別,因而又去 Google 了一下,而後發現了這篇文章,有點茅塞頓開的感受)異步
ok,接着繼續來看 commit 的源碼。
在BackStackRecord
這個類裏看到了具體實現以下:ide
@Override
public int commit() {
return commitInternal(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;
}複製代碼
從上面能夠發現,關鍵在於enqueueAction
這個方法上,因而繼續跟,在FragmentManager
的源碼發現以下:ui
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);
}
}
/**
* 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) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction. return; } throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<>(); } mPendingActions.add(action); scheduleCommit(); } }複製代碼
從上面能夠看出,有三種狀況會致使異常:this
針對方法:spa
commitAllowingStateLoss
方法!isFinishing()
內部(這裏給出瞭解決方案)因此,我的比較推薦的 commit 姿式應該是:code
if (!isFinishing()) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.commitAllowingStateLoss();
}複製代碼
(這裏說明一下,commitAllowingStateLoss
方法不是必定要這樣的,就像在參考文章裏說的,當你肯定你沒法避免那個異常的時候才用這個,官方推薦的是用 commit
;而使用commitAllowingStateLoss
致使的後果在參考文章裏也有說明)
以上,純粹是我的的小總結,也歡迎朋友們對文中不對的地方進行指正。Peace~~
(最後再嘮嗑一句,英語過得去而且有耐心的朋友強烈推薦看一下參考文獻的第一篇,講得比較詳細)
參考文獻