読者です 読者をやめる 読者になる 読者になる

うさがにっき

読書感想文とプログラムのこと書いてきます

FragmentTransactionのcommitとcommitAllowintStateLossの違い

概要

FragmentTransactionのcommit()とcommitAllowintStateLoss()の違いは

  • commit()

状態の保持を保証する

  • commitAllowintStateLoss()

状態の保持を保証しない

詳細

FragmentTransactionにはcommit()メソッドの他にcommitAllowintStateLoss()メソッドがある
名前の通りcommitAllowintStateLoss()は状態を失ってもエラーとしない

これがどういうことか掘り下げていく
ActivityはonSaveInstanceState()メソッドが呼ばれると、現在保持しているFragmentの状態を保持するためにFragmentManagerのsaveAllState()を呼ぶ

public class Activity extends ... {
    ...
    final FragmentManagerImpl mFragments = new FragmentManagerImpl();
    ...
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            // Fragmentの状態をリストア
            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.fragments : null);
        }
    mFragments.dispatchCreate();
    ...
    }
    ...
    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        // Fragmentの状態を保持
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }
    ...

FragmentManagerのsaveAllState()では以下の処理を行う

  1. それまでペンディングされていたトランザクションを実行
  2. アクティブな各Fragmentの状態を保存し、TargetFragmentおよびTargetFragmentCodeがあれば保存
  3. 現在追加されているFragmentのリストを保存
  4. BackStackの情報を保存

Fragmentのリストを保存、BackStackの情報を保存している
もしこの状態保存の後に別のトランザクションが発生し、commit()を実行するとIllegalStateExceptionが発生する
トランザクションによるFragmentの状態変化を保存しなくていい場合は、commitAllowingStateLoss()を使う
これを使った場合はActivityのonSaveInstanceState()メソッドが呼ばれた後かどうかのチェックを行わない

    final class BackStackRecord extends FragmentTransaction... {
        public int commit() {
             return commitInternal(false);
         }
     
         public int commitAllowingStateLoss() {
             return commitInternal(true);
         }

        int commitInternal(boolean allowStateLoss) {
             ...
             mManager.enqueueAction(this, allowStateLoss);
             return mIndex;
        }

    }

mManagerはFragmentManagerImpl
mStateSavedによるチェックをcommitAllowingStateLossからきた場合行わない

    final class FragmentManagerImpl extends FragmentManager...{
        ...
        public void enqueueAction(Runnable action, boolean allowStateLoss) {
            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);
            }        
        }
        ...
        Parcelable saveAllState() {
            // Make sure all pending operations have now been executed to get
            // our state update-to-date.
            execPendingActions();

            mStateSaved = true;

            ...

            return fms;
        }
    ...
    }

参考

tatsu-zine.com