关于Fragment的一些琐事

1,021 阅读8分钟

本篇主要记录Fragment源码分析以及一些容易忽视的问题。基于api28,androidx.fragment:fragment:1.0.0。


Fragment生命周期

image

[TOC]

1.Fragment如何被添加到Activity的?

都知道Fragment的add,replace,hide,show以及最后的commit离不开FragmentTransaction。,而FragmentTransaction最终是通过FragmentManagerImpl#beginTransaction来获取的。下面是Fragmen添加t到Activity的用法。

supportFragmentManager.beginTransaction()
        .add(R.id.item_detail_container, fragment)
        .commit()

FragmentManagerImpl#beginTransaction的实现如下,也就是说FragmentTransaction的实现为BackStackRecord。那么后续的add操作也就基于BackStackRecord。 下文中将BackStackRecord简称为bsr。

public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

那么接着看bsr#add

public FragmentTransaction add(int containerViewId, Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

private void doAddOp(int containerViewId, Fragment fragment,  String tag, int opcmd) {
    fragment.mFragmentManager = mManager;
    if (tag != null) {
        fragment.mTag = tag;
    }
    if (containerViewId != 0) {
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }
    addOp(new Op(opcmd, fragment));
}

void addOp(Op op) {
    mOps.add(op);
}

bsr#add将fragment以及标识为add的OP_ADD标志封装到Op对象并添加到一个叫mOps ArrayList中。 那么add方法至此就分析完毕。接下来是commit方法。

bsr#commit

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

int commitInternal(boolean allowStateLoss) {
    ...
    mCommitted = true;
    mManager.enqueueAction(this, allowStateLoss);
    ...
}

FragmentManager#enqueueAction

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    synchronized (this) {
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<>();
        }
        mPendingActions.add(action);
        scheduleCommit();
    }
}

void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

到此bsrcommit操作被添加到mPendingActions List中,并往主线程handler提交一个叫mExecCommit的Runnable来完成commit操作。由此可知commt操作与Activity生命周期不同步,即在Activity#onCreate中调用commit并不是立即执行的。

mExecCommit的调用栈

Runnable mExecCommit = new Runnable() {
    @Override
    public void run() {
        execPendingActions();
    }
};

public boolean execPendingActions() {
    ensureExecReady(true);
    boolean didSomething = false;
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        } finally {
            cleanupExec();
        }
        didSomething = true;
    }
    doPendingDeferredStart();
    burpActive();
    return didSomething;
}

ensureExecReady(true)操作执行一些动画的取消和创建mTmpRecords,mTmpIsPop这两个类型都为ArrayList。 那么看下while条件

private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isPop) {
    boolean didSomething = false;
    synchronized (this) {
        //刚刚给mPendingActions添加了一个OpGenerator
        if (mPendingActions == null || mPendingActions.size() == 0) {
            return false;
        }
        //从这段代码分析可知generateOpsForPendingActions返回值
        final int numActions = mPendingActions.size();
        for (int i = 0; i < numActions; i++) {
            didSomething |= mPendingActions.get(i).generateOps(records, isPop);
        }
        mPendingActions.clear();
        mHost.getHandler().removeCallbacks(mExecCommit);
    }
    return didSomething;
}

刚刚给mPendingActions添加了一个OpGenerator,而OpGenerator的实现为bsr。那么由此可知generateOpsForPendingActions返回值受bsr#generateOps影响。

bsr#generateOps

public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
    ...
    records.add(this);
    isRecordPop.add(false);
    ...
    return true;
}

generateOps默认返回true,并把bsr添加到records,即mTmpRecords中。 那么接着看while循环体中实现

private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop) {
    ...
    final int numRecords = records.size();
    int startIndex = 0;
    for (int recordNum = 0; recordNum < numRecords; recordNum++) {
        //默认为false
        final boolean canReorder = records.get(recordNum).mReorderingAllowed;
        if (!canReorder) {
            executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
        }
    }
    ...
}

private void executeOpsTogether(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    ...
    executeOps(records, isRecordPop, startIndex, endIndex);
    ...
}

private static void executeOps(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    for (int i = startIndex; i < endIndex; i++) {
        final BackStackRecord record = records.get(i);
        final boolean isPop = isRecordPop.get(i);
        //isPop为false
        if (isPop) {
        ...
        } else {
            record.bumpBackStackNesting(1);
            //执行bsr#executeOps
            record.executeOps();
        }
    }
}

bsr#executeOps

void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        ...
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            case OP_SET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(f);
                break;
            case OP_UNSET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(null);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        ...
    }
    //mReorderingAllowed为false
    if (!mReorderingAllowed) {
        mManager.moveToState(mManager.mCurState, true);
    }
}

由上面bsr#executeOps分析可知由不同的操作调用FragmentManager不同的方法,这里只看add操作,所以这里调用FragmentManager#addFragment。 最后由于mReorderingAllowed为false,调用FragmentManager#moveToState方法。

FragmentManager#addFragment

public void addFragment(Fragment fragment, boolean moveToStateNow) {
    makeActive(fragment);
    if (!fragment.mDetached) {
        synchronized (mAdded) {
            mAdded.add(fragment);
        }
        fragment.mAdded = true;
        fragment.mRemoving = false;
        if (fragment.mView == null) {
            fragment.mHiddenChanged = false;
        }
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment);
        }
    }
}

makeActive操作将fragment添加到mActive SparseArray中。 FragmentManager#makeActive

void makeActive(Fragment f) {
    if (f.mIndex >= 0) {
        return;
    }
    f.setIndex(mNextFragmentIndex++, mParent);
    if (mActive == null) {
        mActive = new SparseArray<>();
    }
    mActive.put(f.mIndex, f);
}

这里说明下mActive存储Fragment时机,由于commit操作是往handler post了一个Runnable。那么导致commit操作和Activty声明周期是不同步的。而 Activity#OnStart()调用时也会调刚才分析commit的调用链(从execPendingActions开始)。也就是说execPendingActions不单单是被我们主动commit调用,而且还被Activity#onStart()调用,其实无论Fragment添加操作被谁调用,后续的主要调用方法都是一样的。总之根据addFragment可知,当前Fragment实例被添加到mActive和mAdded中。

那么接着分析bsr#executeOps中最后一个执行操作,即调用FragmentManager.moveToState(mManager.mCurState, true); 而moveToState(mManager.mCurState,true)最终会调用moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) 所以接下来直接分析moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive)

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
         case Fragment.INITIALIZING:
                    if (newState > Fragment.INITIALIZING) {
                        if (f.mSavedFragmentState != null) {
                            //恢复状态相关
                        }
                        ...
                        //调用Fragmnet#onAttach
                        f.onAttach(mHost.getContext());
                        if (!f.mIsCreated) {
                            //调用Fragment#onCreate
                            f.performCreate(f.mSavedFragmentState);
                        } 
                    }
                case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                //获取Activity提供给Fragment添加的ViewGroup
                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                            }
                            f.mContainer = container;
                            //调用Fragment#onCreateView
                            f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                //将onCreateView中获取的View添加到Activity提供给Fragment添加的ViewGroup中
                                if (container != null) {
                                    container.addView(f.mView);
                                }
                                //如果是hide操作则GONE掉
                                if (f.mHidden) {
                                    f.mView.setVisibility(View.GONE);
                                }
                                //调用Fragment#onViewCreated
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                            }
                            
                        }
                        //调用Fragment#onActivityCreated
                        f.performActivityCreated(f.mSavedFragmentState);
                    }
                    // fall through
                case Fragment.ACTIVITY_CREATED:
                    if (newState > Fragment.ACTIVITY_CREATED) {
                        //调用Fragment#onStart
                        f.performStart();
                    }
                    // fall through
                case Fragment.STARTED:
                    if (newState > Fragment.STARTED) {
                        //调用Fragment#onResume
                        f.performResume();
                       ...
                    }
            }
        } else if (f.mState > newState) {
            switch (f.mState) {
                case Fragment.RESUMED:
                    if (newState < Fragment.RESUMED) {
                        //调用Fragment#onPause
                        f.performPause();
                    }
                case Fragment.STARTED:
                    if (newState < Fragment.STARTED) {
                        //调用Fragment#onStop
                        f.performStop();
                    }
                case Fragment.ACTIVITY_CREATED:
                    if (newState < Fragment.ACTIVITY_CREATED) {
                        //调用Fragment#onDestroyView
                        f.performDestroyView();
                        f.mContainer = null;
                        f.mView = null;
                        f.mViewLifecycleOwner = null;
                        f.mViewLifecycleOwnerLiveData.setValue(null);
                        f.mInnerView = null;
                        f.mInLayout = false;
                    }
                    // fall through
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                        if (!f.mRetaining) {
                            //调用Fragment#onDestory
                            f.performDestroy();
                        } else {
                            f.mState = Fragment.INITIALIZING;
                        }
                        //调用Fragment#onDetach
                        f.performDetach();
                    }
            }
        }
    }         
}            

至此可以解答Fragment如何被添加到Activity中的。通过FragmentTransaction#add(id,fragment)并且commit后。经过一系列调用最终调用到FragmentManager#moveToState,在moveToState根据当前Fragment状态来调用Fragment一系列生命周期方法,在Fragment#performCreateView会调用Fragment#onCreateView,并将获取的View添加到add指定的id中。同时也知道了onCreateView中container参数其实就是add指定id的ViewGroup。而这里知道add原理后,其实hide也就是将Fragment中的View GONE掉。

2.FragmentTransaction的add和replace区别?

add刚才分析过了,就是将Fragment中的View添加到add指定的id的ViewGroup中并执行一系列Fragment生命周期方法。那么replace要分情况。

  1. 如果当前Activity同一个id没有不存Fragment,replace操作和add操作一样。
  2. 如果当前Activity同一个id存在Fragment,replace传递的Fragment实例和已存在的Fragment实例一样,replace无效果
  3. 如果当前Activity同一个id存在Fragment,replace传递的Fragment实例和已存在的Fragment实例不一样,删除旧的fragment,添加新的fragment
BackStackRecord#expandOps
Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
        for (int opNum = 0; opNum < mOps.size(); opNum++) {
            final Op op = mOps.get(opNum);
            switch (op.cmd) {
                case OP_REMOVE:
                case OP_DETACH: {
                    added.remove(op.fragment);
                }
                case OP_REPLACE: {
                    final Fragment f = op.fragment;
                    final int containerId = f.mContainerId;
                    boolean alreadyAdded = false;
                    //如果当前没有Fragment添加到Activity中,added为空
                    for (int i = added.size() - 1; i >= 0; i--) {
                        //如果当前已有Fragment添加到Activity中。
                        final Fragment old = added.get(i);
                        if (old.mContainerId == containerId) {
                            if (old == f) {
                                //如果replace的是同一个Fragment
                                alreadyAdded = true;
                            } else {
                                //不是同一Fragment,删除旧的Fragment
                                final Op removeOp = new Op(OP_REMOVE, old);
                                removeOp.enterAnim = op.enterAnim;
                                removeOp.popEnterAnim = op.popEnterAnim;
                                removeOp.exitAnim = op.exitAnim;
                                removeOp.popExitAnim = op.popExitAnim;
                                mOps.add(opNum, removeOp);
                                added.remove(old);
                                opNum++;
                            }
                        }
                    }
                    if (alreadyAdded) {
                        mOps.remove(opNum);
                        opNum--;
                    } else {
                        //当前Activity中同一个id没有添加Fragment或者同一个id存在Fragment,但replace传递fragment和已有的不是同一个实例,将操作类型replace修改为add
                        op.cmd = OP_ADD;
                        added.add(f);
                    }
                }
                break;
            }
        }
        return oldPrimaryNav;
    }

那么知道add和replace区别后,也就知道add和replace对Fragment生命周期影响

  1. 如果当前Activity同一个id没有不存Fragment,replace操作和add操作一样。

    即执行两者操作生命周期变化:onAttach->onCreate->onCreateView->onActivityCreated->onStart-onResume,

    Fragment所依附的Activity销毁时,执行onPause->onStop->onDestoryView->onDestory->onDetach

  2. 如果当前Activity同一个id存在Fragment,replace传递的Fragment实例和已存在的Fragment实例一样,replace无效果,这点从上面源码中可以看出

  3. 如果当前Activity同一个id存在Fragment,replace传递的Fragment实例和已存在的Fragment实例不一样,删除旧的Fragment,添加新的Fragment,这点从上面代码中也可以看出来。

    旧的Fragment执行 onPause->onStop->onDestoryView->onDestory->onDetach

    新的Fragment执行 onAttach->onCreate->onCreateView->onActivityCreated->onStart-onResume

3.Activity生命周期变化如何通知到Fragment的?

Activity中通知Fragment是在FragmentActivity中各个生命周期方法内通过FragmentController#dispatchxxx方法。 例如FragmentActivity#onPause

//FragmentActivity#onPause
protected void onPause() {
    super.onPause();
    ...
    mFragments.dispatchPause();
}

//FragmentController#dispatchPause()
public void dispatchPause() {
    mHost.mFragmentManager.dispatchPause();
}
//FragmentManager#dispatchPause()
public void dispatchPause() {
    dispatchStateChange(Fragment.STARTED);
}
//FragmentManager#dispatchStateChange()
private void dispatchStateChange(int nextState) {
    try {
        mExecutingActions = true;
        moveToState(nextState, false);
    } finally {
        mExecutingActions = false;
    }
    execPendingActions();
}

最终调用moveToState(nextState,false),前面分析过两个参数的moveToState会调用五个参数的moveToState。在五个参数的moveToState完成生命周期的分发。

4.Fragment中状态保存与恢复

都知道Activity中状态保存和恢复可以使用 onSaveInstanceStateonCreate 或者 onRestoreInstanceState。Fragment中状态保存也用到onSaveInstanceState,但并没有提供 onRestoreInstanceState 。状态恢复可以在Fragment中onCreateonViewCreatedonActivityCreated任意一个。

4.1Fragment状态保存触发时机

FragmentActivity#onSaveInstanceState最终会调用Fragment#onSaveInstanceState

FragmentActivity#onSaveInstanceState
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Parcelable p = mFragments.saveAllState();
    //这里的p实际是FragmentManagerState
    if (p != null) {
        //存储Fragment状态
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}

整个调用链如下:

  1. FragmentActivity#onSaveInstanceState
  2. FragmentController#saveAllState
  3. FragmentManager#saveAllState
  4. FragmentManager#saveFragmentBasicState
  5. Fragment#performSaveInstanceState
  6. Fragment#onSaveInstanceState

整个调用链关于Fragment自身状态和FragmentManager中保存的关于Fragment数据以及开发者在重写Fragment#onSaveInstanceState的数据被保存在FragmentManagerState并返回。其中FragmentManagerState实现了Parcelable

4.2Fragment状态恢复触发时机

当屏幕旋转,FragmentActivity重建,系统先反射创建新的Fragment并从FragmentManagerState中取出之前保存的数据并赋值给新的Fragment,然后执行新Fragment的onAttach...一系列声明周期方法。并将保存的数据传递给Fragmeng的onCreate,onViewCreated,onActivityCreated。

4.2.1Fragment反射创建

FragmentActivity#onCreate FragmentController#restoreAllState FragmentManager#restoreAllState FragmentState#instantiate FragmentContainer#instantiate Fragment#instantiate

当屏幕旋转FragmentActivity重建,从FragmentActivity#onCreate参数bundle取出之前存储的FragmentManagerState并依次调用FragmentController#restoreAllState...,最后反射创建Fragment并从FragmentManagerState中取出保存的数据并赋值给新创建的Fragment。最后将该Fragment保存到FragmentManager#mActive和mAdd中。

这里挑选创建并恢复Fragment代码片段

//FragmentState#instantiate
public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
            Fragment parent, FragmentManagerNonConfig childNonConfig,
            ViewModelStore viewModelStore) {
        if (mInstance == null) {
            final Context context = host.getContext();
            if (mArguments != null) {
                mArguments.setClassLoader(context.getClassLoader());
            }
            if (container != null) {
                //反射创建Fragment
                mInstance = container.instantiate(context, mClassName, mArguments);
            } else {
                mInstance = Fragment.instantiate(context, mClassName, mArguments);
            }
            if (mSavedFragmentState != null) {
                //mSavedFragmentState为bundle对象,这里恢复bundle对象,该对象中保存着Fragment中View状态以及自定义保存的数据。
                mSavedFragmentState.setClassLoader(context.getClassLoader());
                mInstance.mSavedFragmentState = mSavedFragmentState;
            }
            mInstance.setIndex(mIndex, parent);
            mInstance.mFromLayout = mFromLayout;
            mInstance.mRestored = true;
            mInstance.mFragmentId = mFragmentId;
            mInstance.mContainerId = mContainerId;
            mInstance.mTag = mTag;
            mInstance.mRetainInstance = mRetainInstance;
            mInstance.mDetached = mDetached;
            mInstance.mHidden = mHidden;
            mInstance.mFragmentManager = host.mFragmentManager;

            if (FragmentManagerImpl.DEBUG) {
                Log.v(FragmentManagerImpl.TAG, "Instantiated fragment " + mInstance);
            }
        }
        mInstance.mChildNonConfig = childNonConfig;
        mInstance.mViewModelStore = viewModelStore;
        return mInstance;
    }

从上面代码可知为什么在Activity中创建Fragment如果需要传递数据,建议使用setArguments。为了就是在Fragment重建时恢复传递的数据。使用其他方式会导致数据的丢失。

4.2.2 恢复数据

恢复数据调用堆栈

  1. FragmentActivity#onCreate
  2. FragmentController#dispatchCreate
  3. FragmentManager#dispatchCreate
  4. FragmentManager#moveToState//两个参数的moveToState
  5. FragmentManager#moveToState//五个参数的moveToState

这里挑选Fragment#onCreate恢复数据代码片段

  void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
            switch (f.mState) {
                case Fragment.INITIALIZING:
                        ...
                        if (f.mSavedFragmentState != null) {
                           //一系列恢复操作
                        }
                        f.onAttach(mHost.getContext());
                        f.performCreate(f.mSavedFragmentState);
                }
                ...
    }

Frgament反射创建与恢复数据可知f.mSavedFragmentState不为空并且保存的是View中的状态以及自定义保存数据。这里调用performCreate->onCreate并将mSaveFragmentState传递给onCreate,在onCreate中根据传递的mSaveFragmentState(bundle对象)来获取保存的数据。

5.setRetainInstance()的作用是什么?

如果Fragment设置setRetainInstance(true),那么它所依附的Activity重建后该Fragment不会重新销毁并创建。也就是重建后和重建前的实例一样。 setRetainInstance(true) 非正常销毁时不会走Fragment#onDestory() 非正常创建时不会走Fragment#onCreate() 前提是在创建Fragment需要在FragmentManager中根据TAG查找Fragment是否为空,为空则创建添加。

根据设置setRetainInstance(true),可以应用到将Activity中的数据保存到Fragment中,以完成屏幕旋转,以及其他重建Activity时的数据恢复操作。

6.FragmentTransaction各种提交操作介绍?

commit();

commit是将事务操作放到主线程消息队列中,所以它并不是同步操作。commit不允许状态丢失,也就是在onSaveInstanceState之后不允许commit操作。(Can not perform this action after onSaveInstanceState)

commitAllowingStateLoss();

和commit操作一样。区别是允许丢失状态。允许在onSaveInstanceState之后操作事务。

commitNow();

同步执行Fragment事务操作,不允许丢失状态。

commitNowAllowingStateLoss();

同步执行Fragment事务操作,允许丢失状态。

7.开发中容易忽视的问题

7.1状态恢复相关

7.1.1 使用FragmentPagerAdapter时,需要考虑宿主Activity重建带来的问题

在activity使用FragmentPagerAdapter时,随手写出如下代码。其实这样写看似没问题,其实隐藏风险。

private val mFragmentList = mutableListOf<Fragment>()

mFragmentList.add(xxx)
mFragmentList.add(xxx)
mFragmentList.add(xxx)
...
view_pager.setAdapter(supportFragmentManager, mFragmentList)

fun <T : Fragment> ViewPager.setAdapter(fragmentManager: FragmentManager, fragmentList: MutableList<T>) {
    adapter = object : FragmentPagerAdapter(fragmentManager) {
        override fun getItem(position: Int): Fragment {
            return fragmentList[position]
        }

        override fun getCount(): Int {
            return fragmentList.size
        }
    }
}

如果后续通过mFragmentList进行操作,比如刷新第1个位置fragment中的数据。

mFragmentList.get(0).refresh()

那么在activity重建后(屏幕旋转或者后台进程重启)根据mFragmentList去刷新第一个位置的fragment不起作用。具体原因如下:

public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);
    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    ...
    return fragment;
}

在ViewPager加载当前页时,会通过FragmentPagerAdapter调用instantiateItem来加载当前fragment。而当前fragment创建时通过fragmentManager做了一层判断。为空时,才创建,即调用getItem(position)即从mFragmentList中取出fragment。如果不为空直接就复用。

问题就出现在当activity重建时,系统调用activity的onSaveInstanceState()来实现状态的保存,而onSaveInstanceState()中会通过fragmentManager对当前已添加的fragment进行保存。等到系统重建完后在onCreate()中通过fragmentManager来恢复之前保存的fragment。

那么此时即使从新设置了viewPager的adapter。但是instantiateItem()中获取fragment的fragmentManager是同一个。也就是获取到的还是activity重建前的fragment。也就是当前屏幕展示的和mFragmentList中保存的fragment不一致。

解决方案如下: 在每次创建fragment之前判断下。为空则创建,否则不创建。

 mFragmentList.apply {
    addFragment(this, view_pager, 0) {
        //创建fragment实例并返回
    }
    addFragment(this, view_pager, 1) {
        //创建fragment实例并返回
    }
 }
 
inline fun <reified T : Fragment> MutableList<T>.addFragment(fragmentActivity: FragmentActivity, viewGroup: ViewGroup, pos: Int, createFragment: () -> T) {
    val tag = "android:switcher:" + viewGroup.id + ":" + pos
    val fragment: T = fragmentActivity.supportFragmentManager.findFragmentByTag(tag) as? T
            ?: createFragment()
    add(fragment)
}