当FragmentTransaction在add和replace时,它们之间的区别

前言

我们在使用FragmentTransaction的时候,经常会遇到add,replace这两个方法。
如下:

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transition = fragmentManager.beginTransaction();
        FragmentOne fragmentOne = new FragmentOne();
        transition.add(R.id.container_layout,fragmentOne);
//        transition.replace(R.id.container_layout,fragmentOne);
        transition.commit();

在一般情况下,它们之间并没有什么区别,都能达到一样的效果。但是想想,如果真的没有区别的话,Android就完全没有必要同时提供这两个方法了。
我们还是先来看看android中对这两个方法的说明:

FragmentTransaction add (int containerViewId, Fragment fragment, String tag)

Add a fragment to the activity state. This fragment may optionally also have its view (if Fragment.onCreateView returns non-null) inserted into a container view of the activity.
add是把一个fragment添加到activity中的容器container view中。

FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)

Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.
repalce是先remove掉当前相同containerViewId 的所有fragment,然后在add当前这个fragment。

从上面的说明来看,这两个的使用效果基本相同的。光从上面几句话的表述中,也许并不能很好的解释其中的原理。下面我还是从源码的角度来进行说明。

源码分析

FragmentManager是个抽象类,当我们执行getSupportFragmentManager方法时,得到的是它的实现类FragmentManagerImpl。FragmentTransaction同样是抽象类,它的实现类是BackStackRecord类。从BackStackRecord中,我们可以看到add,replace方法。
add方法:

    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);
        return this;
    }

replace方法:

    @Override
    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }

从上面可以看到,add和replace都是调用doAddOp方法,只是在最后的一个参数不同。

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        final Class fragmentClass = fragment.getClass();
        final int modifiers = fragmentClass.getModifiers();
        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                    + " must be a public static class to be  properly recreated from"
                    + " instance state.");
        }

        fragment.mFragmentManager = mManager;

        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }

        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("Can't add fragment "
                        + fragment + " with tag " + tag + " to container view with no id");
            }
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }

        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }

这个方法里面,首先判断传入的fragment方法是不是合法的。
接着,如果传入的参数TAG不为空的话,将它赋值到fragment的mTag中,如:fragment.mTag = tag;
然后判断containerViewId的合法后,执行赋值:fragment.mContainerId = fragment.mFragmentId = containerViewId;
最后将fragment加入到队中,调用addOp方法。

void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }

以上完成了准备阶段,上面貌似没有解决add和replace之间的不同操作,我们继续看BackStackRecord类,知道原来它继承了Runable,run方法才是真正执行的方法。在while循环中的switch case包含了OP_ADD和OP_REPLACE的分支,这不正是刚才方法doAddOp中传入的不同参数吗。所以我们只需要关注这两个case的分支执行的代码就行了。

case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;
case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
                            Fragment old = mManager.mAdded.get(i);
                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                                    "OP_REPLACE: adding=" + f + " old=" + old);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                                + old + " to " + old.mBackStackNesting);
                                    }
                                    mManager.removeFragment(old, transition, transitionStyle);
                                }
                            }
                        }
                    }
                    if (f != null) {
                        f.mNextAnim = enterAnim;
                        mManager.addFragment(f, false);
                    }
                } break;

其中OP_ADD很简单,仅仅就是将fragment加入队列中,我们来
看看OP_REPLACE。
这里面有个for循环,目的是将FragmentManagerImpl中,已经添加的Fragment全部遍历出来,取出与当前fragment中mContainerId相同的fragment,并且remove掉,最后又重新调用addFragment,将它加入到队列中来。这时调用的方法与OP_ADD相同。
在BackStackRecord中有FragmentManagerImpl引用,然后在构造器的参数列表中进行赋值初始化。在FragmentManagerImpl中的addFragment方法,用来保存每次add的Fragment,并将它放在mAdded的容器中。

    public void addFragment(Fragment fragment, boolean moveToStateNow) {
        if (mAdded == null) {
            mAdded = new ArrayList<Fragment>();
        }
        if (DEBUG) Log.v(TAG, "add: " + fragment);
        makeActive(fragment);
        if (!fragment.mDetached) {
            if (mAdded.contains(fragment)) {
                throw new IllegalStateException("Fragment already added: " + fragment);
            }
            mAdded.add(fragment);
            fragment.mAdded = true;
            fragment.mRemoving = false;
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            if (moveToStateNow) {
                moveToState(fragment);
            }
        }
    }

同样与之对应的还有一个removeFragment的方法,用来处理remove Fragment。

public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        final boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            if (mAdded != null) {
                mAdded.remove(fragment);
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.mAdded = false;
            fragment.mRemoving = true;
            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
                    transition, transitionStyle, false);
        }
    }

总结:
在一般的情况下使用add和replace,并没有什么区别,replace会先查找FragmentManager中是否包含相同mContainerId 的fragment,如果有就先remove它,然后再add它。
注意它是根据containerViewId来进行比较的,并不是依据对象是否相等来判断的。在这一点上还需要注意区别。

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