【再探Fragment】从源码看add、replace区别 附popBackStack

1,949 阅读3分钟

整体流程

在上一篇文章从activity和fragment自身这两个视角分析了一下fragment生命周期流程,感兴趣的小伙伴可以去看一下从源码角度分析fragment生命周期流程,在这里附上一个add的流程图看起来更加直观,在里面也会提到哪些步骤add和replace是不一样的

image.png

add和replace

add和replace在源码中主要的不同点提现在

  1. 肯定是add和replace最初的op.mCmd值是不一样的一个是OP_ADD一个是OP_REPLACE
  2. record.expandOps方法中replace是会把OP_REPLACE拆分成OP_REMOVE和OP_ADD然后循环执行fragmentStateManager.moveToExpectedState();不同之处就在这里,replace多了一个remove操作,它会把之前的fragment移出去,至于被移出的fragment的生命周期走到onDestroyView还是onDetach看它的事务是否被加入了返回栈,具体看第三点
  3. computeExpectedState方法获取的期望生命周期不一样
// fragmentStateManager
int computeExpectedState() {
    //mFragmentManagerState的值来源于创建fragmentStateManager的时候,把fragmentManager的生命周期赋值给它
    int maxState = mFragmentManagerState;

    //这个是和fragmentTransaction.setMaxLifecycle配合使用的,这里没用到,默认是RESUMED
    switch (mFragment.mMaxState) {
        case RESUMED:
            // maxState can't go any higher than RESUMED, so there's nothing to do here
            break;
        case STARTED:
            maxState = Math.min(maxState, Fragment.STARTED);
            break;
        case CREATED:
            maxState = Math.min(maxState, Fragment.CREATED);
            break;
        case INITIALIZED:
            maxState = Math.min(maxState, Fragment.ATTACHED);
            break;
        default:
            maxState = Math.min(maxState, Fragment.INITIALIZING);
    }

    //这里貌似是判断是否在xml中引入的fragment
    if (mFragment.mFromLayout) {
        if (mFragment.mInLayout) {
            // Move them immediately to VIEW_CREATED when they are
            // actually added to the layout (mInLayout).
            maxState = Math.max(mFragmentManagerState, Fragment.VIEW_CREATED);
            // But don't move to higher than VIEW_CREATED until the view is added to its parent
            // and the LayoutInflater call has returned
            if (mFragment.mView != null && mFragment.mView.getParent() == null) {
                maxState = Math.min(maxState, Fragment.VIEW_CREATED);
            }
        } else {
            if (mFragmentManagerState < Fragment.ACTIVITY_CREATED) {
                // But while they are not in the layout, don't allow their
                // state to progress upward until the FragmentManager state
                // is at least ACTIVITY_CREATED. This ensures they get the onInflate()
                // callback before being attached or created.
                maxState = Math.min(maxState, mFragment.mState);
            } else {
                // Once the FragmentManager state is at least ACTIVITY_CREATED
                // their state can progress up to CREATED as we assume that
                // they are not ever going to be in layout
                maxState = Math.min(maxState, Fragment.CREATED);
            }
        }
    }
    //如果是add提交事务的话就走不进该判断,而replace由于被拆分成remove和add
    //remove操作在executeOps方法removeFragment的时候就置为false了
    //因此此时add操作:maxState==RESUMED,remove操作:masState==CREATED
    if (!mFragment.mAdded) {
        maxState = Math.min(maxState, Fragment.CREATED);
    }
    if (awaitingEffect == SpecialEffectsController.Operation.LifecycleImpact.ADDING) {
        // Fragments awaiting their enter effects cannot proceed beyond that state
        maxState = Math.min(maxState, Fragment.AWAITING_ENTER_EFFECTS);
    } else if (awaitingEffect == SpecialEffectsController.Operation.LifecycleImpact.REMOVING) {
        // Fragments that are in the process of being removed shouldn't go below that state
        maxState = Math.max(maxState, Fragment.AWAITING_EXIT_EFFECTS);
    } 
    //这里也是同样的道理executeOps方法removeFragment的时候mRemoving会被置为true
    else if (mFragment.mRemoving) {
        //被replace的fragment是否加入了返回栈,加入了maxState==CREATED,否则为INITIALIZING
        if (mFragment.isInBackStack()) {
            // Fragments on the back stack shouldn't go higher than CREATED
            maxState = Math.min(maxState, Fragment.CREATED);
        } else {
            // While removing a fragment, we always move to INITIALIZING
            maxState = Math.min(maxState, Fragment.INITIALIZING);
        }
    }
    return maxState;
}

//moveToExpectedState方法
//通过上面可以知道remove操作得到的期望的生命周期肯定小于当前的生命周期状态,这个时候和add就是完全想反的操作了
//以下代码解释了为什么加入返回栈的只会走到onDestroyView,而未加入的会走detach
case Fragment.CREATED:
    destroyFragmentView();
    mFragment.mState = Fragment.CREATED;
    break;
case Fragment.ATTACHED:
    destroy();
    break;
case Fragment.INITIALIZING:
    detach();
    break;

fragment返回栈

addToBackStack做了什么

public FragmentTransaction addToBackStack(@Nullable String name) {
    if (!mAllowAddToBackStack) {
        throw new IllegalStateException(
                "This FragmentTransaction is not allowed to be added to the back stack.");
    }
    //这里呼应了BackStackRecord的generateOps,会把事务加入到fragmentManager的mBackStack列表中
    mAddToBackStack = true;
    //这是我们自己设置的名字,可以为null
    mName = name;
    return this;
}

popBackStack做了什么

这里就拿最简单的fragmentManager.popBackStack()来讨论

public void popBackStack() {
    //很熟悉的方法enqueueAction,也是向列表中提交事务,不过这里实例化了一个PopBackStackState对象,有必要看一下
    enqueueAction(new PopBackStackState(null, -1, 0), false);
}
private class PopBackStackState implements OpGenerator {
    final String mName;
    final int mId;
    final int mFlags;

    //PopBackStackState也实现了OpGenerator接口,目前name==null,id==-1,flag==0
    PopBackStackState(@Nullable String name, int id, int flags) {
        mName = name;
        mId = id;
        mFlags = flags;
    }
}

目前来看popBackStack和add、replace构建的action是不一样的,一个是BackStackRecord一个是PopBackStackState

构建完成后就都是向fragmentManager的队列中放入action并执行,事务的来源已经不一样了,generateOps当然也不一样,这里就呼应了流程图的这部分:

image.png 来看一下PopBackStackState中的generateOps

@Override
public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
        @NonNull ArrayList<Boolean> isRecordPop) {
     //已省略...
     //目前name==null,id==-1,flag==0
    return popBackStackState(records, isRecordPop, mName, mId, mFlags);
}

核心是popBackStackState现在来看一下

@SuppressWarnings({"unused", "WeakerAccess"}) /* synthetic access */
boolean popBackStackState(@NonNull ArrayList<BackStackRecord> records,
        @NonNull ArrayList<Boolean> isRecordPop, @Nullable String name, int id, int flags) {
    if (mBackStack == null) {
        return false;
    }
    //由于目前name==null,id==-1,flag==0所以会直接进入if的判断中
    if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
        //这里直接把返回栈的最后一个数据放入records列表中并且isRecordPop的数据也为true
        int last = mBackStack.size() - 1;
        if (last < 0) {
            return false;
        }
        //需要注意这里保存的数据是之前提交的事务
        records.add(mBackStack.remove(last));
        isRecordPop.add(true);
    } else {
        int index = -1;
        if (name != null || id >= 0) {
            // If a name or ID is specified, look for that place in
            // the stack.
            index = mBackStack.size() - 1;
            while (index >= 0) {
                BackStackRecord bss = mBackStack.get(index);
                if (name != null && name.equals(bss.getName())) {
                    break;
                }
                if (id >= 0 && id == bss.mIndex) {
                    break;
                }
                index--;
            }
            if (index < 0) {
                return false;
            }
            if ((flags & POP_BACK_STACK_INCLUSIVE) != 0) {
                index--;
                // Consume all following entries that match.
                while (index >= 0) {
                    BackStackRecord bss = mBackStack.get(index);
                    if ((name != null && name.equals(bss.getName()))
                            || (id >= 0 && id == bss.mIndex)) {
                        index--;
                        continue;
                    }
                    break;
                }
            }
        }
        if (index == mBackStack.size() - 1) {
            return false;
        }
        for (int i = mBackStack.size() - 1; i > index; i--) {
            records.add(mBackStack.remove(i));
            isRecordPop.add(true);
        }
    }
    return true;
}

至此对于popBackStack()方法的数据已经构建完成,则继续执行以下流程:

image.png 在executeOps方法会判断是否是出栈如果是则执行executePopOps方法:

//BackStackRecord
//可以看出这里是和当前事务执行相反的操作,比如之前的事务的操作是OP_ADD
//这里判断到是case OP_ADD时是执行remove操作
void executePopOps(boolean moveToState) {
    for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
        final Op op = mOps.get(opNum);
        Fragment f = op.mFragment;
        if (f != null) {
            f.setPopDirection(true);
            f.setNextTransition(FragmentManager.reverseTransit(mTransition));
            // Reverse the target and source names for pop operations
            f.setSharedElementNames(mSharedElementTargetNames, mSharedElementSourceNames);
        }
        switch (op.mCmd) {
            case OP_ADD:
                f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
                mManager.setExitAnimationOrder(f, true);
                mManager.removeFragment(f);
                break;
            case OP_REMOVE:
                f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
                mManager.addFragment(f);
                break;
            case OP_HIDE:
                f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
                mManager.showFragment(f);
                break;

当executeOps执行完成后,所有的准备就全部结束了,后续的操作就和add这些是一样的了,通过moveToExpectedState把fragment移动到期望的生命周期状态。

popBackStack系列方法会调用 popBackStackState 构造 recordsisRecordPop 列表,isRecordPop 的内部元素的值均为true 后续流程和提交事务是一样的,根据 isRecordPop 值的不同选择执行 executePopOpsexecuteOps 方法

最后总结一点:对于popBackStack(String name, int flag),name为 addToBackStack(String name) 的参数,如果只调用popBackStack()name为null,flag为0,通过name能找到回退栈的特定元素,flag可以为 0 或者FragmentManager.POP_BACK_STACK_INCLUSIVE,0 表示只弹出该元素以上的所有元素,POP_BACK_STACK_INCLUSIVE 表示弹出包含该元素及以上的所有元素。这里说的弹出所有元素包含回退这些事务,具体可以参考popBackStackState方法中的源码。