Android commit 和 commitAllowingStateLoss 的區(qū)別

1615230-60be89c602040f87.jpg

fragment 基本上是每個項(xiàng)目都會用到,一般我們會這么寫:

    getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.fragment_container, new MyFragment())
            .commit();

但是有時候會報如下錯誤信息:

    Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

意思就是說我們不能在調(diào)用onSaveInstanceState進(jìn)行commit操作。網(wǎng)上的解決辦法是使用commitAllowingStateLoss替換commit。確實(shí)是不報錯了,但是為什么呢?

來,開始我們的偵探之旅吧?。ㄓ悬c(diǎn)繞,請一步步往下看)

首先找到FragmentTransaction類。

    public abstract class FragmentTransaction {
        public abstract int commit();
        public abstract int commitAllowingStateLoss();
    }

原來commitcommitAllowingStateLoss是抽象方法。繼續(xù)往下找。

    final class BackStackRecord extends FragmentTransaction implements
            FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
        @Override
        public int commit() {
            return commitInternal(false);
        }

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

發(fā)現(xiàn)BackStackRecord類繼承了FragmentTransaction??梢钥闯?,不同之處只有commitInternal的參數(shù)。感覺離真相又近了一步,繼續(xù)往下看:

    int commitInternal(boolean allowStateLoss) {    
        // ...不顯示無關(guān)代碼
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

commitInternal方法內(nèi),只有mManager.enqueueAction(this, allowStateLoss);使用了該布爾值參數(shù)。離真相還差一點(diǎn)了,繼續(xù)推測:

    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");
            }
            // ...無關(guān)代碼
        }
    }

有了突破性進(jìn)展!此處對allowStateLoss值進(jìn)行了判斷。checkStateLoss按照命名意思是校驗(yàn)狀態(tài)。離真相僅剩一步了!

    private void checkStateLoss() {
        if (isStateSaved()) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        if (mNoTransactionsBecause != null) {
            throw new IllegalStateException(
                    "Can not perform this action inside of " + mNoTransactionsBecause);
        }
    }

    @Override
    public boolean isStateSaved() {
        // See saveAllState() for the explanation of this.  We do this for
        // all platform versions, to keep our behavior more consistent between
        // them.
        return mStateSaved || mStopped;
    }

這里會拋出異常信息,明顯就是文章開頭碰到的異常錯誤信息!isStateSaved()方法也一同顯示了。到了此時,可以明白commit是會對狀態(tài)進(jìn)行檢測,并拋出異常;而commitAllowingStateLoss方法只是不進(jìn)行狀態(tài)檢測,因此不會拋出異常。這明顯是有點(diǎn)逃避問題,那么這個狀態(tài)是什么判斷而得出的呢?

    Parcelable saveAllState() {
        // ...無關(guān)代碼
        mStateSaved = true;
        // ...無關(guān)代碼
    }

我們主要看mStateSaved變量。在saveAllState 方法內(nèi),把mStateSaved賦值為 true。有意思的是saveAllState是在ActivityFragmentActivity內(nèi)的onSaveInstanceState方法調(diào)用的。

總結(jié):

有點(diǎn)繞~最后在此理順整體思路:在ActivityFragmentActivity內(nèi)的onSaveInstanceState方法保存了fragment的狀態(tài)。在onSaveInstanceState方法后調(diào)用commitcommitAllowingStateLoss會引起一種問題:因內(nèi)存不足而把不顯示在前臺的 activity (帶有 fragment)銷毀,之后用戶再回到此 activity 頁面時,是會丟失在onSaveInstanceState后調(diào)用commit方法提交的頁面狀態(tài)信息!
不同的只是調(diào)用commit會報錯,調(diào)用commitAllowingStateLoss不報錯(睜一只眼閉一只眼)。

谷歌在commitAllowingStateLoss方法的注釋上也寫明,調(diào)用此方法會有丟失頁面狀態(tài)信息的風(fēng)險。

Like {@link #commit} but allows the commit to be executed after an
activity's state is saved. This is dangerous because the commit can
be lost if the activity needs to later be restored from its state, so
this should only be used for cases where it is okay for the UI state
to change unexpectedly on the user.

  1. 一般情況下,盡量提早調(diào)用 commit 方法,比如說onCreate();
  2. 異步回調(diào)中避免使用commit;
  3. 不得已的情況,可以使用commitAllowingStateLoss替代commit。畢竟報錯奔潰,比頁面狀態(tài)信息丟失更嚴(yán)重;

推薦:

Android commit 和 commitAllowingStateLoss 區(qū)別及應(yīng)用場景

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容