关闭

Fragment的添加和删除及其源码分析

标签: Fragmentaddreplaceremove源码解析
247人阅读 评论(0) 收藏 举报
分类:

Android API中FragmentManager和FragmentTransaction这两个类中提供了大量的对Fragment进行操作的方法,这里做记录和整理。

查找Fragment

其中FragmentManager中维护了一个队列,用于存放Fragment实例,我们添加的Fragment可以通过以下方法查找

Fragment的查找

  • getFragmentManager()
    • findFragmentById():根据布局xml文件中声明的id查找对应的Fragment;
    • findFragmentByTag():根据动态添加Fragment的TAG值,查找对应的Fragment;
getFragmentManager().findFragmentById(R.id.fragment_list);
getFragmentManager().findFragmentById(TAG);

查找嵌套在Fragment内部的Fragment

  • getChildFragmentManager():当Fragment中嵌套着另外一个Fragment,注意这时候要使用getChildFragmentManager()获取对应的FragmentManager,否则会查找不到该Fragment导致空指针异常
TestListFragment testListFragment = (TestListFragment) getChildFragmentManager().findFragmentById(R.id.fragment_list);

Fragment的添加,删除

添加Fragment

  • add():向Activity中添加Fragment;
  • remove():删除Fragment,当该Fragment没有入栈,那么该Fragment的实例将会被删除,反之,该实例不会被删除;
  • replace():其实是先remove()然后调用add();

源码分析

  • add()方法解析
    add()方法是FragmentTransaction中的方法,我们从这个类开始,进入FragmentTransaction后,发现该类是一个抽象类。查找该类的实现类:BackStackRecord,该类还实现了Runnable接口,我们来看看该类中的add()方法

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

    发现add()的三个重载方法都调用doAppOp()这个方法,另外有一个地方我们需要注意,该方法的最后都加了一个标记参数:OP_ADD,我们来看看这个方法

    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
            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 (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);
    

    我们可以看到该方法将参数进行了传递,最后调用了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++;
        }

    该方法中,只是进行了op对象的传递,和动画的参数传递,然后就没有下文了,难道到这就断了?并不是,在添加Fragment的时候,最后都会调用commit()方法,添加的Fragment才会真正的被添加上去,那我们来看看commit()方法的源码

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

    其内部调用的是commitInternal(),继续看源码

    int commitInternal(boolean allowStateLoss) {
            if (mCommitted) throw new IllegalStateException("commit already called");
            if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this);
            mCommitted = true;
            if (mAddToBackStack) {
                mIndex = mManager.allocBackStackIndex(this);
            } else {
                mIndex = -1;
            }
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }

    该方法的最后,将该类加入了一个Action队列mManager.enqueueAction(this, allowStateLoss);,继续看

    public void enqueueAction(Runnable action, boolean allowStateLoss) {
            if (!allowStateLoss) {
                checkStateLoss();
            }
            synchronized (this) {
                if (mActivity == null) {
                    throw new IllegalStateException("Activity has been destroyed");
                }
                if (mPendingActions == null) {
                    mPendingActions = new ArrayList<Runnable>();
                }
                mPendingActions.add(action);
                if (mPendingActions.size() == 1) {
                    mActivity.mHandler.removeCallbacks(mExecCommit);
                    mActivity.mHandler.post(mExecCommit);
                }
            }
        }

    该方法中将BackStackRecord加入了mPendingActions集合,接着来看这句代码mActivity.mHandler.post(mExecCommit);,Handler执行mExecCommit,我们再来看看mExecCommit是什么

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

    其也是一个Runnable,run方法中执行了execPendingActions(),我们再来看看这个方法:

    /**
         * Only call from main thread!
         */
        public boolean execPendingActions() {
            if (mExecutingActions) {
                throw new IllegalStateException("Recursive entry to executePendingTransactions");
            }
    
            if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
                throw new IllegalStateException("Must be called from main thread of process");
            }
    
            boolean didSomething = false;
    
            while (true) {
                int numActions;
    
                synchronized (this) {
                    if (mPendingActions == null || mPendingActions.size() == 0) {
                        return didSomething;
                    }
    
                    numActions = mPendingActions.size();
                    if (mTmpActions == null || mTmpActions.length < numActions) {
                        mTmpActions = new Runnable[numActions];
                    }
                    mPendingActions.toArray(mTmpActions);
                    mPendingActions.clear();
                    mActivity.mHandler.removeCallbacks(mExecCommit);
                }
    
                mExecutingActions = true;
                for (int i=0; i<numActions; i++) {
                    mTmpActions[i].run();
                    mTmpActions[i] = null;
                }
                mExecutingActions = false;
                didSomething = true;
            }
        }

    该方法中mPendingActions与数组mTmpActions完成了置换,(不明白的可以去看下,toArray()方法的内部实现)最后for循环依次执行run方法。终于回归正轨,这里看下BackStackRecord中的run方法

    public void run() {
            if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
    
            if (mAddToBackStack) {
                if (mIndex < 0) {
                    throw new IllegalStateException("addToBackStack() called after commit()");
                }
            }
    
            bumpBackStackNesting(1);
    
            Op op = mHead;
            while (op != null) {
                switch (op.cmd) {
                    case OP_ADD: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    } break;
                    case OP_REPLACE: {
                        Fragment f = op.fragment;
                        if (mManager.mAdded != null) {
                            for (int i=0; i<mManager.mAdded.size(); i++) {
                                Fragment old = mManager.mAdded.get(i);
                                if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                                        "OP_REPLACE: adding=" + f + " old=" + old);
                                if (old.mContainerId == f.mContainerId) {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = op.exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                                + old + " to " + old.mBackStackNesting);
                                    }
                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
                                }
                            }
                        }
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    } break;
                    case OP_REMOVE: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.exitAnim;
                        mManager.removeFragment(f, mTransition, mTransitionStyle);
                    } break;
                    case OP_HIDE: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.exitAnim;
                        mManager.hideFragment(f, mTransition, mTransitionStyle);
                    } break;
                    case OP_SHOW: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        mManager.showFragment(f, mTransition, mTransitionStyle);
                    } break;
                    case OP_DETACH: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.exitAnim;
                        mManager.detachFragment(f, mTransition, mTransitionStyle);
                    } break;
                    case OP_ATTACH: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        mManager.attachFragment(f, mTransition, mTransitionStyle);
                    } break;
                    default: {
                        throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                    }
                }
    
                op = op.next;
            }
    
            mManager.moveToState(mManager.mCurState, mTransition,
                    mTransitionStyle, true);
    
            if (mAddToBackStack) {
                mManager.addBackStackState(this);
            }
        }
    

    内容比较庞大,但是还是决定贴出来,这里要说一下我们在之前提到的OP_ADD,这就是添加的标记,它的作用在该方法中提现出来了,除此以外,还有别的状态标记:replace,remove,hide,show,detach,attach这些;最后这些对应的标记下都调用了FragmentManagerImp中对应的方法。这里就不再贴源码了,感兴趣的朋友可以自行查看。
    该方法最后两段代码稍微提一下:

    mManager.moveToState(mManager.mCurState, mTransition,
                    mTransitionStyle, true);

    这段代码中的moveToState方法体现了Fragment在不同生命周期状态下所进行的一些操作,大家可以看看源码

    if (mAddToBackStack) {
                mManager.addBackStackState(this);
            }

    这段代码用来设置回退栈

  • remove()方法解析
    结论上面已经贴出,这里只贴下证明结论的源码和简单解析

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

    这句代码final boolean inactive = !fragment.isInBackStack();,获取当前fragment是否加入回退栈的状态,如果没加入将会进入方法体执行mAdded.remove(fragment);,删除fragment实例

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

    上面源码直接可以看出,replace方法先调用了mManager.removeFragment(old, mTransition, mTransitionStyle);,最后调用了mManager.addFragment(f, false);,这也验证了我们之前的结论。

    这也是add方法与replace方法的区别

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:28370次
    • 积分:831
    • 等级:
    • 排名:千里之外
    • 原创:57篇
    • 转载:0篇
    • 译文:0篇
    • 评论:5条
    文章分类
    最新评论