异常如下:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
在使用fragment、dialogFragment等控件时可能会遇到以上异常。原因正如抛出的异常所说一样:不能在onSaveInstanceState之后执行该操作。即不能执行commit()操作,包括dialogFragment的show()、dismiss(),fragment中fragmentTransaction的commit()等等。
接下来,找一下崩溃源码,追一下崩溃相关的源码流程。
对应的崩溃源码流程:
比如调用DialogFragment#show()
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit(); // 这里这里
}
commit的实现方法:
@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);
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss); // 这里这里
return mIndex;
}
最终到了FragmentManager中,接着看enqueueAction方法
public void enqueueAction(Runnable 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<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
private void checkStateLoss() {
if (mStateSaved) { // 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);
}
}
从整个流程中看到,只要在commit时,mStateSaved已经被置为true,则会抛出该异常。
那么问题来了,都有哪些方法会保存状态,也就是把mStateSaved置为true呢?
- onSaveInstanceState
既然异常中说了和onSaveInstanceState方法有关,那么就先看看该方法做了什么:
Activity.java(android-22)
/**
* Called to retrieve per-instance state from an activity before being killed
* so that the state can be restored in {@link #onCreate} or
* {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
* will be passed to both).
* ........... 这里省略的说明对理解onSaveInstanceState很重要,有点多,自己去看吧
*/
protected void onSaveInstanceState(Bundle outState) {
// 保存window的Hierarchy状态
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
// 保存fragment的状态。如果感兴趣可以再看看mFragments.saveAllState()的实现,也是保存状态
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
FragmentActivity.java(android-22)
/**
* Save all appropriate fragment state.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
}
可以继续跟下 mFragments.saveAllState(),
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
execPendingActions();
if (HONEYCOMB) { // 这个解释也可以好好看看,说明了不同版本保存状态的时机是不同的
// As of Honeycomb, we save state after pausing. Prior to that
// it is before pausing. With fragments this is an issue, since
// there are many things you may do after pausing but before
// stopping that change the fragment state. For those older
// devices, we will not at this point say that we have saved
// the state, so we will allow them to continue doing fragment
// transactions. This retains the same semantics as Honeycomb,
// though you do have the risk of losing the very most recent state
// if the process is killed... we'll live with that.
mStateSaved = true; // 在这里保存了
}
....省略....
}
另外,可以再看看FragmentActivity#onStop()方法:
@Override
protected void onStop() {
super.onStop();
mStopped = true;
mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
mFragments.dispatchStop();
}
继续追继续追,最后也是到FragmentManager中
public void dispatchStop() {
// See saveAllState() for the explanation of this. We do this for
// all platform versions, to keep our behavior more consistent between
// them.
mStateSaved = true; // 这里这里
moveToState(Fragment.STOPPED, false);
}
是的,yeke直接看FragmentManager中mStateSaved的变化时机就可以了,比如下边几个,更详细的跟下源码即可:
public void noteStateNotSaved() {
mStateSaved = false; // 这里
}
public void dispatchCreate() {
mStateSaved = false; // 这里
moveToState(Fragment.CREATED, false);
}
public void dispatchActivityCreated() {
mStateSaved = false; // 这里
moveToState(Fragment.ACTIVITY_CREATED, false);
}
public void dispatchStart() {
mStateSaved = false; // 这里
moveToState(Fragment.STARTED, false);
}
public void dispatchResume() {
mStateSaved = false; // 这里
moveToState(Fragment.RESUMED, false);
}
public void dispatchPause() {
moveToState(Fragment.STARTED, false);
}
public void dispatchStop() {
// See saveAllState() for the explanation of this. We do this for
// all platform versions, to keep our behavior more consistent between
// them.
mStateSaved = true; // 这里
moveToState(Fragment.STOPPED, false);
}
目前为止,基本了解了该异常的抛出相关的源码流程了。
下篇博客这里这里继续分析下如何避免该异常,以及其他相关细节。
转载请标明来源:http://blog.csdn.net/rflyee/article/details/74719551