Android开发Android开发经验谈Android知识

Can not perform this action afte

2017-10-25  本文已影响356人  Rflyee

异常如下:

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呢?

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

上一篇下一篇

猜你喜欢

热点阅读