Can not perform this action after onSaveInstanceState:问题分析

问题描述:
当手机进入黑屏的时候,APP蓝牙断开连接,APP收到蓝牙断开连接,通过EventBus方式调用commit方法让UI界面跳转到蓝牙连接界面,此时会报Can not perform this action after onSaveInstanceState:错误,且不会正常跳转到指定界面,从网上找到解决方法是:commitAllowingState()方法替换commit()方法,问题解决。
根据网上资料,总结如下,以便后续参考。
首先找到FragmentTransaction方法的实现类

    final class BackStackRecord extends FragmentTransaction implements
                      FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator 

而FragmentTransaction类中的commit方法与commitAllowState调用如下:

public int commit() {
        return commitInternal(false);
}

public int commitAllowingStateLoss() {
        return commitInternal(true);
}

两个方法最终调用commitInternal方法,只是commit传入了false,另一个传入了true;
进入commitInternal方法:

int commitInternal(boolean allowStateLoss) {
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
            dump("  ", null, pw, null);
            pw.flush();
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
}

在此方法中只有mManager.enqueueAction(this, allowStateLoss)代码用到了参数allowStateLoss,进入mManager.enqueueAction方法如下:

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();
        }
}

当调用commit时allowStateLoss为true,其会进入如下代码:

 if (!allowStateLoss) {
            checkStateLoss();
 }
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);
        }
    }

根据上面内容,只要mStateSaved为true就会报Can not perform this action after onSaveInstanceState错误,接下来分析,当手机黑屏的时候在哪里给mStateSaved设置成了true;

 Parcelable saveAllState() {
       ...
        execPendingActions();

        mStateSaved = true;
        mSavedNonConfig = null;
        ...
 }

这个方法分别是在FragmentActivity的onSaveInstanceState()中被调用,而onSaveInstanceState()的调用时机是在onPause()之后onStop()之前,这样可以总结出来当Activity的onSaveInstanceState()方法调用之后如果调用了该Activity的FragmentTransaction的commit方法,就会抛出异常。

在使用中此处蓝牙断开连接,是通过EventBus来进行MainActivity的Fragment切换,由于在进入锁屏后会调用会调用onPause(), onSaveInstanceState(), onStop()方法,因此mStateSaved = true, 最终会报上述错误,修改为commitAllowingState()方法,问题解决。

参考博文:
commit和commitAllowingStateLoss方法的区别

发布了13 篇原创文章 · 获赞 2 · 访问量 577

猜你喜欢

转载自blog.csdn.net/qq_42806685/article/details/104404799