[Exploring Fragment again] See the difference between add and replace from the source code with popBackStack

Overall process

In the previous article, I analyzed the fragment life cycle process from the perspectives of activity and fragment itself. Interested partners can go and analyze the fragment life cycle process from the perspective of source code . Here is a flowchart of add to see. It is more intuitive, and it will also mention which steps add and replace are different.

image.png

add and replace

The main difference between add and replace in the source code is mentioned in

  1. It must be that the original op.mCmd values ​​of add and replace are different, one is OP_ADD and the other is OP_REPLACE
  2. The replace in the record.expandOps method will split OP_REPLACE into OP_REMOVE and OP_ADD and then execute fragmentStateManager.moveToExpectedState() in a loop; the difference is here, replace has an additional remove operation, which will remove the previous fragment. The life cycle of the removed fragment goes to onDestroyView or onDetach to see if its transaction is added to the return stack, see the third point for details
  3. The expected life cycle obtained by the computeExpectedState method is different
// 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 return stack

what addToBackStack does

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;
}

what popBackStack does

Here is the simplest fragmentManager.popBackStack() to discuss

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;
    }
}

At present, the actions constructed by popBackStack and add and replace are different. One is BackStackRecord and the other is PopBackStackState.

After the construction is completed, actions are put into the fragmentManager's queue and executed. The source of the transaction is different, and the generateOps is of course different. This part of the flow chart is echoed here:

image.pngTake a look at generateOps in PopBackStackState

@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);
}

The core is popBackStackState now let's take a look

@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;
}

So far, the data for the popBackStack() method has been constructed, and the following process is continued:

image.pngIn the executeOps method, it will determine whether it is popped from the stack. If so, execute the executePopOps method:

//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;

When executeOps is executed, all preparations are over, and the subsequent operations are the same as add. The fragment is moved to the desired life cycle state through moveToExpectedState.

The popBackStack series of methods will call popBackStackState to construct recordsand isRecordPoplist, isRecordPopand the values ​​of the internal elements are all true. The subsequent process is the same as the commit transaction, and isRecordPopthe executePopOpsor executeOpsmethod is selected according to the different value.

A final summary: for popBackStack(String name, int flag), the name is the parameter of addToBackStack(String name), if you only call popBackStack(), the name is null, and the flag is 0, the specific element of the back stack can be found by name, flag Can be 0. Alternatively FragmentManager.POP_BACK_STACK_INCLUSIVE, 0 means that only all elements above this element are popped, and all elements POP_BACK_STACK_INCLUSIVEthat contain this element and above are popped. Popping all elements mentioned here includes rolling back these transactions. For details, please refer to the source code in the popBackStackState method.

Guess you like

Origin juejin.im/post/7117567128336597005