Android修炼系列(28),FragmentManager 和它的事务官 FragmentTransaction

这是一篇很久以前写的文章了,当时是发布在了别的平台,今天搬来掘金。说实话,使用掘金也快一年了,这里没有各种乱七八糟的东西,界面也简单干净,真的很适合安安静静地写点东西。

正文

本节主要分析下FragmentManager与FragmentTransaction的内部代码,了解一下当我们提交事务时,两者是怎么协调处理的

二、FragmentManager的介绍

关于FragmentManager的内容我们可以查看官网FragmentManager,当然更详细的我们也可以查看FragmentManager源码,在这里我就简单的介绍下,当我们在Activity中要获取FragmentManager时,我们直接调用的getFragmentManager()方法

        FragmentManager fm = getFragmentManager();
复制代码

如果你只想使用FragmentManager,那么这样一行代码就完全足够了,但是如果你想知其所以然,那么有一些地方就需要注意了,因为FragmentManager是一个抽象类,所以我们获取到的只是FragmentManager的一个实现类(子类FragmentManagerImpl)对象而已,具体的获取过程我们可以接着往下看,这是我们超类Activity类的部分代码

        final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

        /**
        * Return the FragmentManager for interacting with fragments associated
        * with this activity.
         */
        public FragmentManager getFragmentManager() {
            return mFragments.getFragmentManager();
        }
复制代码

这是我们FragmentController类,我们发现其又调用了mHost.getFragmentManagerImpl()方法

        private final FragmentHostCallback<?> mHost;

        public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
            return new FragmentController(callbacks);
        }
        public FragmentManager getFragmentManager() {
            return mHost.getFragmentManagerImpl();
        }
复制代码

这是我们的抽象类FragmentHostCallback< E >,通过对象组合,最后调用方法的返回值即一个FragmentManagerImpl对象

        final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();

        FragmentManagerImpl getFragmentManagerImpl() {
            return mFragmentManager;
        }
复制代码

而FragmentManagerImpl又何许东西也?这就是我们最后获取到的FragmentManagerImpl类,是一个内部类

    public abstract class FragmentManager {
        ...//省略代码
        final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
            ...
        }
        ...
    }
复制代码

知道了这些我们就明白了,其实我们平时所使用的findFragmentById都是FragmentManagerImpl的方法,多态嘛

    final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
            /**注意只是根据需要展示了FragmentManagerImpl的部分代码*/
            ArrayList<Fragment> mAdded;//管理着一个fragment队列
            ArrayList<BackStackRecord> mBackStack;//管理着一个回退栈

            @Override public FragmentTransaction beginTransaction() {
                return new BackStackRecord(this);
            }
            //addFragment、removeFragment、hideFragment、showFragment
            //detachFragment、attachFragment常用方法
            public void addFragment(Fragment fragment, boolean moveToStateNow) {
                 //将fragment添加到容器里,并改变相应的标志位
                ...
            }
            //findFragmentById、findFragmentByTag常用方法
            public Fragment findFragmentById(int id) {
                //从容器里根据id取出相应的fragment
                ...
            }
复制代码

在这里我只是简单展示了一下FragmentManagerImpl的极少量代码,让我们心里先有个大致印象,当我们介绍事务FragmentTransaction的时候,我们会接触到FragmentManagerImpl更多的方法,明白当我们提交一个事务时,两者是怎么协调处理的。

三、FragmentTransaction的分析

上面我们说了FragmentManager,通过beginTransaction()方法,我们便能开启一个事务,之后就是我们的一些普通操作了

        private void addFragment4() {
            FragmentManager fm = getFragmentManager();
            //在这里为了直观展示,就加一个局部变量
            FragmentTransaction fragmentTransaction = fm.beginTransaction();
            MyFragment4 fragment4 = (MyFragment4) fm.findFragmentById(R.id.activity5_fragment);
            if(fragment4 == null){
                fragment4 = new MyFragment4();
                fragmentTransaction.add(R.id.activity5_fragment, fragment4)
                        //根据需要是否要加入到回退栈
                        .addToBackStack(null)
                        .commit();
            }
        }
复制代码

我们知道,fm.beginTransaction()实际调用的是其子类FragmentManagerImpl的方法,其实就是个多态思想

        @Override public FragmentTransaction beginTransaction() {
            return new BackStackRecord(this);
        }
复制代码

通过官网FragmentTransaction我们可以知道,FragmentTransaction也是一个抽象类,我们所使用的事务即其子类BackStackRecord类,类中代码非常多,我们可以查看BackStackRecord源码,在这里,我就根据我们常用的add并commit的流程来简单介绍下BackStackRecord,并了解一下其与FragmentManagerImpl是怎么协调工作的,这是我们的BackStackRecord类

    final class BackStackRecord extends FragmentTransaction implements 
                FragmentManager.BackStackEntry, Runnable {
            //在这里我们持有FragmentManagerImpl对象
            //当然也就能根据需要调用FragmentManagerImpl方法了
            final FragmentManagerImpl mManager;

            public BackStackRecord(FragmentManagerImpl manager) {
                mManager = manager;
            }

        }
复制代码

这是我们的FragmentManagerImpl类,当我们在Activity中beginTransaction()时,我们知道我们创建了一个事务BackStackRecord,并将自身引用传递了过去

    final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
            /**注意只是根据需要展示了FragmentManagerImpl的部分代码*/
            ArrayList<Fragment> mAdded;//管理着一个fragment队列

            @Override public FragmentTransaction beginTransaction() {
                return new BackStackRecord(this);
            }
        }
复制代码

之后我们会调用BackStackRecord类的add(int var1, Fragment var2)方法

        public FragmentTransaction add(int containerViewId, Fragment fragment) {
            //doAddOp(...)这是一个私有方法
            //在方法里会创建一个Op对象
            //Op对象的作用就是记录一次操作的动作和Fragment引用以及操作使用的动画
            doAddOp(containerViewId, fragment, null, OP_ADD);
            return this;
        }
复制代码

之后我们接着调用BackStackRecord类的addToBackStack(null)加入回退栈方法

        public FragmentTransaction addToBackStack(String name) {
            if (!mAllowAddToBackStack) {
                throw new IllegalStateException("This FragmentTransaction is not allowed to be added to the back stack.")
            }
            //我们发现其实这个方法只是改变了标志位,并没有实际的逻辑代码
            //这也是我们为什么必须在commit之前加入回退栈的原因
            //因为commit之后加入,没有改变标志位的事务就不会被提交
            mAddToBackStack = true;
            mName = name;
            return this;
        }
复制代码

之后我们接着在BackStackRecord类中调用commit()方法,提交我们的本次的事务

        public int commit() {
            return commitInternal(false);
        }
        //如果提交时,生命周期处于Saving Activity之后
        //那么使用commit就会由于丢失信息从而抛出错误
        //如果不需要保存信息,可以使用commitAllowingStateLoss
        public int commitAllowingStateLoss() {
            return commitInternal(true);
        }
        //在这里我们就简单看下allowStateLoss为false的情况
        int commitInternal(boolean allowStateLoss) {
            ...//省略部分代码
            if (mAddToBackStack) {
                //使用mAvailBackStackIndices和mBackStackIndices两个数组
                //来为BackStackRecord分配Index
                mIndex = mManager.allocBackStackIndex(this);
            } else {
                Index = -1;
            }
            //这是我们最后提交的方法,会调用FragmentManagerImpl类的enqueueAction方法
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
复制代码

那我们接下来就看下FragmentManagerImpl类的allocBackStackIndex(…)方法,当加入返回栈并在commit事务之前

        // Must be accessed while locked.
        ArrayList<BackStackRecord> mBackStackIndices;
        ArrayList<Integer> mAvailBackStackIndices;

        public int allocBackStackIndex(BackStackRecord bse) {
            synchronized (this) {
                if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
                    if (mBackStackIndices == null) {
                        mBackStackIndices = new ArrayList<BackStackRecord>();
                    }
                    int index = mBackStackIndices.size();
                    mBackStackIndices.add(bse);
                    return index;
                } else {
                    int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
                    mBackStackIndices.set(index, bse);
                    return index;
                }
            }
        }
复制代码

这是我们最后commit提交后调用的FragmentManagerImpl类的enqueueAction(…)方法

        ArrayList<Runnable> mPendingActions;//每提交一个事务都在不同线程里
        Runnable mExecCommit = new Runnable() {
            @Override
            public void run() {
                //这个方法我就不细写了,其主要作用
                //遍历mPendingActions管理的事务线程
                //并调用每个线程事务(BackStackRecord)类的run方法
                //随后一个个移除相应的事务线程
                execPendingActions();
            }
        };

        public void enqueueAction(Runnable action, boolean allowStateLoss) {
            if (!allowStateLoss) {
                checkStateLoss();
            }
            synchronized (this) {
                if (mDestroyed || mHost == null) {
                    throw new IllegalStateException("Activity has been destroyed");
                }
                if (mPendingActions == null) {
                    //这是管理事务的线程集合,每个线程即代表着一个待处理的事务操作
                    mPendingActions = new ArrayList<Runnable>();
                }
                mPendingActions.add(action);
                if (mPendingActions.size() == 1) {
                    mHost.getHandler().removeCallbacks(mExecCommit);
                    //mHost.getHandler()返回一个handler对象
                    //即handler.post(Runnable action)
                    //所以会调用mExecCommit的run方法
                    mHost.getHandler().post(mExecCommit);
                }
            }
        }
复制代码

这就是我们的BackStackRecord类的run方法,在这里我们根据Op对象所携带的信息,判断进行哪种操作,随后再调用FragmentManagerImpl的方法,moveToState(…)和是否需要将事务添加到回退栈

        public void run() {
                ...
                //根据OP的信息调用相应的操作,例如当我们add操作
                switch (op.cmd) {
                    case OP_ADD:
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        //将当前操作的fragment添加到FragmentManager管理的列表中
                        mManager.addFragment(f, false);
                        break;
                    case OP_REMOVE:
                        Fragment f = op.fragment;
                        f.mNextAnim = op.exitAnim;
                        mManager.removeFragment(f, mTransition, mTransitionStyle);
                        break;
                     ...
                }
                ...
                mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
                if (mAddToBackStack) {
                        //将本次事务添加到回退栈
                       mManager.addBackStackState(this);
                }
            }
复制代码

那么最后我们再接着回来看下FragmentManagerImpl类的部分方法

            public void addFragment(Fragment fragment, boolean moveToStateNow) {
                //将fragment添加到管理队列中,并改变相应的标志位
                ...
            }
            //在回退栈中添加一个事务BackStackRecord
            void addBackStackState(BackStackRecord state) {
                if (mBackStack == null) {
                    mBackStack = new ArrayList<BackStackRecord>();
                }
                mBackStack.add(state);
                //回调onBackStackChanged
                reportBackStackChanged();
            }
            //这个方法非常重要,每当Fragment的生命周期中状态改变,都会被调用
            //这也保证了Fragment与Activity能够保持同步
            //具体的代码,感兴趣的可以查看源码了解下
            void moveToState(int newState, int transit, int transitStyle, boolean always) {
               //Fragment的状态切换的操作逻辑
               ...
            }
复制代码

到这里分析就已经结束了,开启并提交一个事务,是否将事务添加到返回栈两种情况,我们合二为一的做了介绍,下面我们再来看一下,当我们按返回键时,当有事务在返回栈的情况下,事务是怎么被弹出栈的,这是我们Activity的代码

        public void onBackPressed() {
            if (mActionBar != null && mActionBar.collapseActionView()) {
                return;
            }
            //如果我们Fragment的事务回退栈还有事务能被弹出则返回true,否则返回false
            if (!mFragments.getFragmentManager().popBackStackImmediate()) {
                //经过一些其他条件的判断,最终会调用finish()方法
                finishAfterTransition();
            }
        }
复制代码

下面我们来接着看下FragmentManagerImpl类的popBackStackImmediate(…)方法

         @Override public boolean popBackStackImmediate() {
            checkStateLoss();
            //如果存在待处理的事务,则直接返回true
            executePendingTransactions();
            //参数是固定的,注释看下面
            return popBackStackState(mHost.getHandler(), null, -1, 0);
        }
复制代码

这是FragmentManagerImpl类的popBackStackState(…)方法

        //1往左移0位,实际还是1,并且0&1=0 (按位与)
        public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;

        boolean popBackStackState(Handler handler, String name, int id, int flags) {
            //如果我们的回退栈为null,那么直接返回false退出Activity
            if (mBackStack == null) {
                return false;
            }
            //当返回键的时候,传参是固定的,所以直接走这个分支
            if(if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0)) {
                int last = mBackStack.size()-1;
                if (last < 0) {
                    return false;
                }
                //将我们管理的回退栈最顶层的remove掉
                final BackStackRecord bss = mBackStack.remove(last);
                SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
                SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
                //作用是循环所有Op,找到第一个被删除的fragment
                //和最后一个被添加的fragment
                bss.calculateBackFragments(firstOutFragments, lastInFragments);
                //这是主要方法,注释看下面
                bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
                //FragmentManagerImpl管理着一个ArrayList<OnBackStackChangedListener> 集合
                //这个方法作用便是循环遍历onBackStackChanged()方法
                reportBackStackChanged();
            }else {
                ...
            }
        }
复制代码

这是我们需要的BackStackRecord类的popFromBackStack(…)方法

        public TransitionState popFromBackStack(boolean doStateMove, TransitionState state
                , SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
            ...
            //根据事务的Op携带的信息,知道事务回退栈里的事务是什么操作,例如add
            //知道栈里的事务是的类型了,当然back时候就知道怎么相应处理了
            //例如事务A是add而来,那么back后执行remove;
            //如果事务A是replace而来,那么back后执行先remove掉新的后再add进替换掉那个old的
            Op op = mTail;
            while (op != null) {
                switch (op.cmd) {
                    case OP_ADD: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.popExitAnim;
                        //因为回退栈里的事务里Op标识说明这是添加操作
                        //所以本次back键直接将这个事务removeFragment即可
                        mManager.removeFragment(f
                            , FragmentManagerImpl.reverseTransit(mTransition)
                            , mTransitionStyle);
                    }
                    break;
                    ...//其他操作情况
                }
            }
            ...
        }
复制代码

好了,本文结束。

猜你喜欢

转载自juejin.im/post/7039963206990725134