FragmentTransaction 的commit, commitNow,commitAllowingStateLoss

1,203 阅读2分钟
FragmentTransaction ft = fragmentManager.beginTransaction();
      ft.commit();
      ft.commitAllowingStateLoss();
      ft.commitNow();
      ft.commitNowAllowingStateLoss();

FragmentTransaction提交事务提供了上述四种方法,我们该怎么用呢?

commit() vs commitAllowingStateLoss()

commit()commitAllowingStateLoss()在实现上唯一的不同就是当你调用commit()的时候, FragmentManger会检查mDestory, mHost 如果mHost ==null, 就抛出IllegalStateException.

看源码知道mHost 是在FragmentActivity的onDestroy 中被置空的(注释5,6,7),也就是说FragmentActivity onDestroy  之后再commit的话,就会抛出异常(注释2,3),commitAllowingStateLoss() 提交则是直接return(注释4),

那为什么commitAllowingStateLoss可能会丢失fragmentManager状态呢?

即save之后任何被添加或被移除的Fragments,   

源码可知,fragmentManager的状态是在onsaveInstance中保存的,ondestory的时候已经保存了,所以之后提交的不会被保存

BackStackRecord.java

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

 FragmentManager.java

void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            if (mHost == null) {//2
                if (mDestroyed) {//3
                    throw new IllegalStateException("FragmentManager has been destroyed");
                } else {
                    throw new IllegalStateException("FragmentManager has not been attached to a "
                            + "host.");
                }
            }
            checkStateLoss();
        }
        synchronized (mPendingActions) {
            if (mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;//注释4
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            mPendingActions.add(action);
            scheduleCommit();
        }
    }

 void dispatchDestroy() {
        mDestroyed = true;//5
        execPendingActions(true);
        endAnimatingAwayFragments();
        dispatchStateChange(Fragment.INITIALIZING);
        mHost = null;//6
        mContainer = null;
        mParent = null;
...
}

FragmentActivity.java

  protected void onDestroy() {
        super.onDestroy();
        mFragments.dispatchDestroy();//7
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }

 @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        markFragmentsCreated();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);//8
        }

commit() VS commitNow()

commit  可以将action加入回退栈, 将action加入到队列中,用handler异步执行

commitNow 不可以加入回退栈,  立即执行

  1. 如果你需要同步的操作, 并且你不需要加到back stack里, 使用commitNow().
    support library在FragmentPagerAdapter里就使用了commitNow()来保证在更新结束的时候, 正确的页面被加上或移除.

  2. 如果你操作很多transactions, 并且不需要同步, 或者你需要把transactions加在back stack里, 那就使用commit().

  3. 如果你希望在某一个指定的点, 确保所有的transactions都被执行, 那么使用executePendingTransactions().

    BackStackRecord.java

    @Override public void commitNow() { disallowAddToBackStack(); mManager.execSingleAction(this, false); }