AndroidX Fragment探究-狀態保存和恢復

概述

我們知道Activity提供了onSaveInstanceStateonRestoreInstanceState回調方法用於狀態保存和恢復,同樣FragmentActivity和Fragment也支持狀態保存和恢復,FragmentActivity會在適當的時機,通過FragmentManagerImpl通知Fragment進行保存操作,接下來從源碼中跟蹤這個通知過程。

源碼探究

文中源碼基於’androidx.fragment:fragment:1.1.0’

狀態保存

FragmentActivity重寫了onSaveInstanceState方法:
[FragmentActivity.java]

protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    // ···
    // 調用FragmentController的saveAllState方法獲取Parcelable
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        // 將Parcelable保存進outState,FRAGMENTS_TAG值爲"android:support:fragments"
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    // ···
}

outState中以FRAGMENTS_TAG爲key存儲數據。

FragmentController的saveAllState方法中又調用FragmentManagerImpl的saveAllState方法:
[FragmentManagerImpl.java]

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

    // 標記狀態保存爲true
    mStateSaved = true;

    if (mActive.isEmpty()) {
        return null;
    }

    // First collect all active fragments.
    int size = mActive.size();
    ArrayList<FragmentState> active = new ArrayList<>(size);
    boolean haveFragments = false;
    for (Fragment f : mActive.values()) {
        if (f != null) {
            if (f.mFragmentManager != this) {
                throwException(new IllegalStateException(
                        "Failure saving state: active " + f
                                + " was removed from the FragmentManager"));
            }

            haveFragments = true;

            // 創建FragmentState用於保存Fragment中的成員變量值
            FragmentState fs = new FragmentState(f);
            active.add(fs);

            // fs.mSavedFragmentState此時默認爲空
            if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                // 派發通知fragment進行數據保存
                fs.mSavedFragmentState = saveFragmentBasicState(f);

                // 若有setTargetFragment,則進行TargetFragment相關數據保存
                if (f.mTargetWho != null) {
                    Fragment target = mActive.get(f.mTargetWho);
                    if (target == null) {
                        throwException(new IllegalStateException(
                                "Failure saving state: " + f
                                        + " has target not in fragment manager: "
                                        + f.mTargetWho));
                    }
                    if (fs.mSavedFragmentState == null) {
                        fs.mSavedFragmentState = new Bundle();
                    }
                    putFragment(fs.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG, target);
                    if (f.mTargetRequestCode != 0) {
                        fs.mSavedFragmentState.putInt(
                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                f.mTargetRequestCode);
                    }
                }

            } else {
                fs.mSavedFragmentState = f.mSavedFragmentState;
            }

            if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
                    + fs.mSavedFragmentState);
        }
    }

    if (!haveFragments) {
        if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
        return null;
    }

    ArrayList<String> added = null;
    BackStackState[] backStack = null;

    // Build list of currently added fragments.
    // 保存mAdded集合中的Fragment唯一ID
    size = mAdded.size();
    if (size > 0) {
        added = new ArrayList<>(size);
        for (Fragment f : mAdded) {
            // f.mWho爲Fragment實例化時生成的唯一UUID
            added.add(f.mWho);
            if (f.mFragmentManager != this) {
                throwException(new IllegalStateException(
                        "Failure saving state: active " + f
                                + " was removed from the FragmentManager"));
            }
            if (DEBUG) {
                Log.v(TAG, "saveAllState: adding fragment (" + f.mWho
                        + "): " + f);
            }
        }
    }

    // Now save back stack.
    // 若有addToBackStack,則進行回退棧相關數據保存
    if (mBackStack != null) {
        size = mBackStack.size();
        if (size > 0) {
            backStack = new BackStackState[size];
            for (int i = 0; i < size; i++) {
                // 創建BackStackState用於保存BackStackRecord的數據
                backStack[i] = new BackStackState(mBackStack.get(i));
                if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                        + ": " + mBackStack.get(i));
            }
        }
    }

    // 創建FragmentManagerState用於保存該FragmentManagerImpl整體數據
    FragmentManagerState fms = new FragmentManagerState();
    fms.mActive = active;
    fms.mAdded = added;
    fms.mBackStack = backStack;
    if (mPrimaryNav != null) {
        fms.mPrimaryNavActiveWho = mPrimaryNav.mWho;
    }
    fms.mNextFragmentIndex = mNextFragmentIndex;
    return fms;
}

該方法中創建FragmentState集合用以保存每個Fragment的數據,String集合保存每個Fragment的唯一ID,BackStackState數組保存每個BackStackRecord的數據,最終創建FragmentManagerState用以保存以上所有數據,並返回添加進Bundle中。

接着看saveFragmentBasicState方法,該方法中進行更詳細的數據保存,返回Bundle賦值給FragmentState的mSavedFragmentState成員保存:
[FragmentManagerImpl.java]

Bundle saveFragmentBasicState(Fragment f) {
    Bundle result = null;

    if (mStateBundle == null) {
        mStateBundle = new Bundle();
    }
    // 觸發Fragment的onSaveInstanceState回調方法;通知Fragment的子Fragment進行狀態保存
    f.performSaveInstanceState(mStateBundle);
    // Lifecycle架構組件回調通知
    dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
    if (!mStateBundle.isEmpty()) {
        result = mStateBundle;
        mStateBundle = null;
    }

    // 若該fragment設置了view,則進行相關視圖樹的保存
    if (f.mView != null) {
        saveFragmentViewState(f);
    }
    if (f.mSavedViewState != null) {
        if (result == null) {
            result = new Bundle();
        }
        // 添加視圖樹數據
        result.putSparseParcelableArray(
                FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
    }
    if (!f.mUserVisibleHint) {
        if (result == null) {
            result = new Bundle();
        }
        // Only add this if it's not the default value
        // 不可見時保存mUserVisibleHint
        result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
    }

    return result;
}

該方法中會通過performSaveInstanceState方法觸發Fragment的onSaveInstanceState回調方法,Fragment子類可重寫該方法保存數據。若該Fragment有設置view,還將通過Fragment的mInnerView成員的saveHierarchyState方法進行視圖樹的保存。

在進行了這一系列的保存後,數據都整合添加進Bundle,由ActivityClientRecord的state成員保存,同時FragmentManagerImpl的mStateSaved被標記爲true。

狀態恢復

進入FragmentActivity的onCreate方法:
[FragmentActivity.java]

protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);

    if (savedInstanceState != null) {
        // 當savedInstanceState不爲空時,取出FRAGMENTS_TAG對應的數據
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        // 將數據交給FragmentManagerImpl進行狀態恢復
        mFragments.restoreSaveState(p);

        // Check if there are any pending onActivityResult calls to descendent Fragments.
        if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
            mNextCandidateRequestIndex =
                    savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
            int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
            String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
            if (requestCodes == null || fragmentWhos == null ||
                        requestCodes.length != fragmentWhos.length) {
                Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
            } else {
                mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
                for (int i = 0; i < requestCodes.length; i++) {
                    mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
                }
            }
        }
    }

    if (mPendingFragmentActivityResults == null) {
        mPendingFragmentActivityResults = new SparseArrayCompat<>();
        mNextCandidateRequestIndex = 0;
    }

    super.onCreate(savedInstanceState);

    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    mFragments.dispatchCreate();
}

在onCreate中,若savedInstanceState不爲空,則取出FRAGMENTS_TAG對應的數據,通過FragmentManagerImpl的restoreSaveState方法進行狀態恢復。

接着看restoreSaveState方法:
[FragmentManagerImpl.java]

void restoreSaveState(Parcelable state) {
    // If there is no saved state at all, then there's nothing else to do
    if (state == null) return;
    FragmentManagerState fms = (FragmentManagerState)state;
    if (fms.mActive == null) return;

    // First re-attach any non-config instances we are retaining back
    // to their saved state, so we don't try to instantiate them again.
    // 處理setRetainInstance相關的Fragment的狀態恢復
    for (Fragment f : mNonConfig.getRetainedFragments()) {
        if (DEBUG) Log.v(TAG, "restoreSaveState: re-attaching retained " + f);
        FragmentState fs = null;
        for (FragmentState fragmentState : fms.mActive) {
            if (fragmentState.mWho.equals(f.mWho)) {
                fs = fragmentState;
                break;
            }
        }
        if (fs == null) {
            // 若RetainedFragment對應的RetainedFragment不存在,則需要移除該RetainedFragment
            if (DEBUG) {
                Log.v(TAG, "Discarding retained Fragment " + f
                        + " that was not found in the set of active Fragments " + fms.mActive);
            }
            // We need to ensure that onDestroy and any other clean up is done
            // so move the Fragment up to CREATED, then mark it as being removed, then
            // destroy it.
            moveToState(f, Fragment.CREATED, 0, 0, false);
            f.mRemoving = true;
            moveToState(f, Fragment.INITIALIZING, 0, 0, false);
            continue;
        }
        fs.mInstance = f;
        f.mSavedViewState = null;
        f.mBackStackNesting = 0;
        f.mInLayout = false;
        f.mAdded = false;
        f.mTargetWho = f.mTarget != null ? f.mTarget.mWho : null;
        f.mTarget = null;
        if (fs.mSavedFragmentState != null) {
            // 恢復RetainedFragment的數據
        fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
            f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                    FragmentManagerImpl.VIEW_STATE_TAG);
            f.mSavedFragmentState = fs.mSavedFragmentState;
        }
    }

    // Build the full list of active fragments, instantiating them from
    // their saved state.
    mActive.clear();
    for (FragmentState fs : fms.mActive) {
        if (fs != null) {
            // 實例化Fragment,並用FragmentState保存的數據來初始化Fragment
            Fragment f = fs.instantiate(mHost.getContext().getClassLoader(),
                    getFragmentFactory());
            f.mFragmentManager = this;
            if (DEBUG) Log.v(TAG, "restoreSaveState: active (" + f.mWho + "): " + f);
            // 添加fragment至mActive成員中
            mActive.put(f.mWho, f);
            // Now that the fragment is instantiated (or came from being
            // retained above), clear mInstance in case we end up re-restoring
            // from this FragmentState again.
            fs.mInstance = null;
        }
    }

    // Build the list of currently added fragments.
    mAdded.clear();
    if (fms.mAdded != null) {
        for (String who : fms.mAdded) {
            // 獲取前面剛剛創建並保存的fragment
            Fragment f = mActive.get(who);
            if (f == null) {
                throwException(new IllegalStateException(
                        "No instantiated fragment for (" + who + ")"));
            }
            f.mAdded = true;
            if (DEBUG) Log.v(TAG, "restoreSaveState: added (" + who + "): " + f);
            if (mAdded.contains(f)) {
                throw new IllegalStateException("Already added " + f);
            }
            synchronized (mAdded) {
                // 添加進mAdded成員中
                mAdded.add(f);
            }
        }
    }

    // Build the back stack.
    // 後退棧數據恢復
    if (fms.mBackStack != null) {
        mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
        for (int i=0; i<fms.mBackStack.length; i++) {
            BackStackRecord bse = fms.mBackStack[i].instantiate(this);
            if (DEBUG) {
                Log.v(TAG, "restoreAllState: back stack #" + i
                        + " (index " + bse.mIndex + "): " + bse);
                LogWriter logw = new LogWriter(TAG);
                PrintWriter pw = new PrintWriter(logw);
                bse.dump("  ", pw, false);
                pw.close();
            }
            mBackStack.add(bse);
            if (bse.mIndex >= 0) {
                setBackStackIndex(bse.mIndex, bse);
            }
        }
    } else {
        mBackStack = null;
    }

    if (fms.mPrimaryNavActiveWho != null) {
        mPrimaryNav = mActive.get(fms.mPrimaryNavActiveWho);
        dispatchParentPrimaryNavigationFragmentChanged(mPrimaryNav);
    }
    this.mNextFragmentIndex = fms.mNextFragmentIndex;
}

該方法中利用保存在FragmentManagerState中的數據進行狀態恢復,其中在利用FragmentState恢復Fragment數據時,會將mSavedFragmentState賦值給Fragment的mSavedFragmentState成員。
mSavedFragmentState中保存了Fragment中的自定義存儲數據和視圖樹狀態

之後隨着FragmentActivity調度Fragment顯示,在Fragment的各生命週期階段中,可以使用mSavedFragmentState保存的數據恢復。

進入FragmentManagerImpl的生命週期狀態調度方法moveToState:
[FragmentManagerImpl.java]

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
                 boolean keepActive) {
    // ···
    if (f.mState <= newState) {
        // ···
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (newState > Fragment.INITIALIZING) {
                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                    if (f.mSavedFragmentState != null) {
                        f.mSavedFragmentState.setClassLoader(mHost.getContext()
                                .getClassLoader());
                        // 獲取保存的視圖樹狀態
                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                FragmentManagerImpl.VIEW_STATE_TAG);
                        Fragment target = getFragment(f.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG);
                        f.mTargetWho = target != null ? target.mWho : null;
                        if (f.mTargetWho != null) {
                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                        }
                        if (f.mSavedUserVisibleHint != null) {
                            f.mUserVisibleHint = f.mSavedUserVisibleHint;
                            f.mSavedUserVisibleHint = null;
                        } else {
                            // 獲取保存的可見狀態
                            f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                                    FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                        }
                        if (!f.mUserVisibleHint) {
                            f.mDeferStart = true;
                            if (newState > Fragment.ACTIVITY_CREATED) {
                                newState = Fragment.ACTIVITY_CREATED;
                            }
                        }
                    }

                    // ···
                    
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    } else {
                        f.mParentFragment.onAttachFragment(f);
                    }
                    dispatchOnFragmentAttached(f, mHost.getContext(), false);

                    if (!f.mIsCreated) {
                        dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
                        // 觸發Fragment的onCreate回調方法,並傳入bundle
                        f.performCreate(f.mSavedFragmentState);
                        dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                    } else {
                        // 派發Fragment的子Fragment的狀態恢復
                        f.restoreChildFragmentState(f.mSavedFragmentState);
                        f.mState = Fragment.CREATED;
                    }
                }
                // fall through
            case Fragment.CREATED:
                // We want to unconditionally run this anytime we do a moveToState that
                // moves the Fragment above INITIALIZING, including cases such as when
                // we move from CREATED => CREATED as part of the case fall through above.
                if (newState > Fragment.INITIALIZING) {
                    ensureInflatedFragmentView(f);
                }

                if (newState > Fragment.CREATED) {
                    if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                    if (!f.mFromLayout) {
                        ViewGroup container = null;
                        if (f.mContainerId != 0) {
                            // ···
                        }
                        f.mContainer = container;
                        // 觸發Fragment的onCreateView回調方法,並傳入bundle
                        f.performCreateView(f.performGetLayoutInflater(
                                f.mSavedFragmentState), container, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mInnerView = f.mView;
                            f.mView.setSaveFromParentEnabled(false);
                            if (container != null) {
                                container.addView(f.mView);
                            }
                            if (f.mHidden) {
                                f.mView.setVisibility(View.GONE);
                            }
                            // 觸發Fragment的onViewCreated回調方法,並傳入bundle
                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                    false);
                            // Only animate the view if it is visible. This is done after
                            // dispatchOnFragmentViewCreated in case visibility is changed
                            f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                                    && f.mContainer != null;
                        } else {
                            f.mInnerView = null;
                        }
                    }

                    // 觸發Fragment的onActivityCreated回調方法,並傳入bundle
                    f.performActivityCreated(f.mSavedFragmentState);
                    dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                    if (f.mView != null) {
                        // 調用mInnerView的restoreHierarchyState方法傳入mSavedViewState進行視圖樹狀態恢復。
                        // 然後回調onViewStateRestored方法並傳入bundle。
                        f.restoreViewState(f.mSavedFragmentState);
                    }
                    // mSavedFragmentState賦值爲空
                    f.mSavedFragmentState = null;
                }
                // fall through
            case Fragment.ACTIVITY_CREATED:
                if (newState > Fragment.ACTIVITY_CREATED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                    dispatchOnFragmentStarted(f, false);
                }
                // fall through
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    dispatchOnFragmentResumed(f, false);
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
        }
    } else if (f.mState > newState) { /* ··· */ }
    // ···
}

從該方法中可以看出,在Fragment的生命週期回調方法中收到的savedInstanceState入參即爲Fragment的mSavedFragmentState,在派發完成後又會將mSavedFragmentState賦值爲空。

commit & commitAllowingStateLoss

這裏看下提交事務的兩個方法:commit、commitAllowingStateLoss,顧名思義,它們一個在提交時不允許狀態丟失、另一個允許。

[BackStackRecord.java]

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

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

都調用了同一個方法commitInternal,只是傳入參數不同,一個傳false,另一個傳true。

繼續看commitInternal方法:
[BackStackRecord.java]

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

allowStateLoss參數直接傳入添加事務隊列的方法。

[FragmentManagerImpl.java]

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    if (!allowStateLoss) {
        // 不允許狀態丟失時,首先進行檢查
        checkStateLoss();
    }
    synchronized (this) {
        if (mDestroyed || mHost == null) {
            // 當FragmentActivity已經destroyed或持有FragmentHostCallback爲空時,若不允許狀態丟失,將拋出異常。
            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();
    }
}

是否允許狀態丟失,就是在添加事務進隊列前,檢查狀態,以及是否拋出異常。

進入checkStateLoss方法:
[FragmentManagerImpl.java]

private void checkStateLoss() {
    // 檢查狀態,判斷是否拋異常
    if (isStateSaved()) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
}

@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.
    // onSaveInstanceState時mStateSaved將置爲true,onStop時mStopped將置爲true
    return mStateSaved || mStopped;
}

不允許在執行onSaveInstanceState回調方法後再添加Fragment事務操作。因爲Fragment的狀態保存是在onSaveInstanceState階段中,此後若再變更FragmentManagerImpl中的Fragment,這些Fragment的狀態不會進行保存。

onSaveInstanceState回調在Android P及以上版本觸發時機在onStop之後,在低版本觸發時機在onStop之前。

總結

FragmentActivity在onSaveInstanceState回調方法中,調用FragmentManagerImpl通知其管理的Fragment進行狀態保存。保存過程中會觸發Fragment的onSaveInstanceState回調,可通過重寫該方法保存自定義數據。若Fragment有設置view,還將進行視圖樹的保存。

FragmentActivity在onCreate回調方法中,判斷savedInstanceState參數是否有值,之後通知FragmentManagerImpl進行狀態恢復,實例化Fragment並將存儲着狀態的bundle賦值給mSavedFragmentState成員。

在Fragment從創建到顯示的生命週期狀態生長階段,通過mSavedFragmentState成員來恢復數據和視圖樹,會將mSavedFragmentState作爲入參調用對應生命週期回調方法,完成後再清除mSavedFragmentState。Fragment子類可在onCreate和onActivityCreated中恢復自定義數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章