Crash:Can not perform this action after onSaveInstanceState

堆棧信息

bug0.png

版本分布

bug1.png

機型

bug2.png

問題分析

0,Activity->onBackPressed

public void onBackPressed() {
        if (!mFragments.popBackStackImmediate()) {
            finish();
        }
    }

1,FragmentManager->popBackStackImmediate

/**
     * Like {@link #popBackStack()}, but performs the operation immediately
     * inside of the call.  This is like calling {@link #executePendingTransactions()}
     * afterwards.
     * @return Returns true if there was something popped, else false.
     */
    public abstract boolean popBackStackImmediate();

2,FragmentManagerImpl->popBackStackImmediate

@Override
    public boolean popBackStackImmediate() {
        checkStateLoss();
        executePendingTransactions();
        return popBackStackState(mHost.getHandler(), null, -1, 0);
    }

3,FragmentManagerImpl->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);
        }
    }

NOTE:崩潰的原因就是mStateSaved=true拋出了IllegalStateException,需要找到mStateSaved在哪些地方被賦值為true

4,FragmentManagerImpl->saveAllState

Parcelable saveAllState() {
        // Make sure all pending operations have now been executed to get
        // our state update-to-date.
        execPendingActions();

        mStateSaved = true;//賦值
        .....
}

mSateSaved只會在saveAllState()中被重新賦值為true,繼續跟進saveAllState()在哪些場景下會被調用

5, Activity->onSaveInstanceState

    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        Parcelable p = mFragments.saveAllState();//在這被調用
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

可以得出初步的結論是當Activity調用onSaveInstanceState,間接改變了FragmentManagerImpl中mStateSaved的值,onBackPressed()被調用時會導致FragmentManagerImpl檢查mStateSaved值,為true就拋出異常導致崩潰

6,關于onSaveInstanceState

這一篇介紹了onSaveInstanceState的調用時機,所以分析可能的操作步驟是,Activity->Home(或者電源鍵)->回Activity->onBackPressed。
當用戶回道回到Activity時,生命周期必然會經過onResume,mStateSaved狀態會重置成false,onBackPressed就不會引起崩潰,這就是矛盾。

解決方案

反射FragmentActivity中的FragmentController對象,調用noteStateNotSaved方法,修改mStateSaved的值為false。
這樣的做的原因是:用戶點擊back鍵的目的是退出界面或者dismiss當前界面的dialog(或者其他浮層),對于前者不用考慮狀態保存的問題,對于后者Activity的生命周期會重新onResume,不會影響Fragment狀態保存。

@Override
    public void onBackPressed() {
        fixBug();
        super.onBackPressed();
    }
private void fixBug() {
        try {
            Class clz = BaseActivity.class;
            while (clz != FragmentActivity.class) {
                clz = clz.getSuperclass();
            }
            Field field = clz.getDeclaredField("mFragments");
            field.setAccessible(true);
            Object object = field.get(this);//獲取mFragments對象
            Class<?> fieldClazz = object.getClass();
            Method method = fieldClazz.getMethod("noteStateNotSaved");
            method.setAccessible(true);
            method.invoke(object);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容