Can not perform this action after onSaveInstanceState异常源码分析

来源:互联网 发布:用homebrew安装mysql 编辑:程序博客网 时间:2024/06/04 20:03

转载请标明来源:http://blog.csdn.net/rflyee/article/details/74719551

异常如下:

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的实现方法:

@Overridepublic 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. */@Overrideprotected 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()方法:
@Overrideprotected 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

阅读全文
1 0
原创粉丝点击