美文网首页
Fragment相关

Fragment相关

作者: sankemao | 来源:发表于2018-05-31 17:05 被阅读15次

    fragment基本操作:

     getSupportFragmentManager().beginTransaction()
                        .add(id, fragment, tag)
                        .addToBackStack(null)
                        .commit();
    

    以下就v4包下的FragmentManager进行源码分析。

    1、FragmentManager#beginTransaction

    该方法返回一个BackStackRecord对象
    该对象内部有一个成员变量mOps:

    ArrayList<Op> mOps = new ArrayList<>();
    

    Op是一个封装了fragment操作以及转场动画的bean:

        static final class Op {
            int cmd;//dvfragment的操作,如add remove replace等
            Fragment fragment;
            int enterAnim;
            int exitAnim;
            int popEnterAnim;
            int popExitAnim;
        }
    

    2、BackStackRecord#add(id, fragment, tag)

    该方法会将add操作封装成一个Op对象,然后存入mOps集合中。

    3、 BackStackRecord#addToBackStack(null)

    @Override
        public FragmentTransaction addToBackStack(String name) {
            if (!mAllowAddToBackStack) {
                throw new IllegalStateException(
                        "This FragmentTransaction is not allowed to be added to the back stack.");
            }
            mAddToBackStack = true;
            mName = name;
            return this;
        }
    

    注意mAddToBackStack 这个变量,很关键!该方法最重要的是让这个变量设为true。它的作用下面会讲到。

    4、BackStackRecord#commit()

    该方法最终调用commitInternal:

    int commitInternal(boolean allowStateLoss) {
            if (mCommitted) throw new IllegalStateException("commit already called");
            mCommitted = true;
            if (addToBackStack) {
                //关键1
                mIndex = mManager.allocBackStackIndex(this);
            } else {
                mIndex = -1;
            }
            //关键2
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
    

    如果调用了步骤3的addToBackStack方法,addToBackStack此时为true,进入判断执行关键1处代码,它的源码为:

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

    刚开始这段代码看的我是云里雾里,没办法,只能写个demo来debug运行不断分析了:
    demo中不停的开启新fragment,且加入回退栈,此时mAvailBackStackIndices=null,走第一个判断,所有事务(BackStackRecord)都会添加到mBackStackIndices 集合中,并不会进else判断。
    然后多次按返回键回退几个fragment,紧接着重新开启fragment,此时才会进入else判断,意味着mAvailBackStackIndices这个集合此时不再为空且有内容了,更巧的是它的集合长度等于mAvailBackStackIndices的长度,那么mAvailBackStackIndices在哪里初始化并存放内容的呢?搜索mAvailBackStackIndices = new,找到下面这段代码,并debug验证了每次按返回键移除fragment都会执行这段代码:

        public void freeBackStackIndex(int index) {
            synchronized (this) {
                mBackStackIndices.set(index, null);
                if (mAvailBackStackIndices == null) {
                    //在这里初始化了
                    mAvailBackStackIndices = new ArrayList<Integer>();
                }
                if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
                //最后添加到mAvailBackStackIndices 后返回的index,会陆续添加到mAvailBackStackIndices中。
                mAvailBackStackIndices.add(index);
            }
        }
    

    总结下mBackStackIndicesmAvailBackStackIndices的操作:
    前者会将BackStackRecord对象存入,这很正常;后者作用是:在从mBackStackIndices集合中移除BackStackRecord对象的时候,将该被移除的BackStackRecord对象所在原集合中的index存入mAvailBackStackIndices,注意加粗的移除,方式并非直接remove,而是ArrayList.set(index,null)。
    为什么不直接用mBackStackIndices,添加就add,移除就remove呢?
    我猜想可能是mBackStackIndices是ArrayList,ArryList基于数组实现,这样避免了释放数组空间并再次开辟的耗时,达到空间换时间提高效率的目的。
    至于mBackStackIndices有啥用?好像跟什么adb命令,打印输出Activity信息啥的有关......走错码头了,看关键2吧。

    5、FragmentManager#enqueueAction(OpGenerator action, boolean allowStateLoss)

    关键2处代码调用mManager.enqueueAction(this, allowStateLoss),参数this即BackStackRecord对象。

    public void enqueueAction(OpGenerator 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<>();
                }
                //将action添加到mPendingActions集合中
                mPendingActions.add(action);
                //执行action任务
                scheduleCommit();
            }
        }
    

    scheduleCommit()方法很简单,它利用handler,将任务发送到主线程的任务队列,执行execPendingActions():

    public boolean execPendingActions() {
            ensureExecReady(true);
    
            boolean didSomething = false;
            while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
                mExecutingActions = true;
                try {
                    removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
                } finally {
                    cleanupExec();
                }
                didSomething = true;
            }
    
            doPendingDeferredStart();
            burpActive();
    
            return didSomething;
        }
    
    

    while循环的判断条件执行generateOpsForPendingActions:

    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
                ArrayList<Boolean> isPop) {
            boolean didSomething = false;
            synchronized (this) {
                //当集合为空或没内容时,返回false,防止while循环无限轮询
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    return false;
                }
    
                final int numActions = mPendingActions.size();
                for (int i = 0; i < numActions; i++) {
                    //这里是关键,且generateOps返回值关系到上面的while循环
                    didSomething |= mPendingActions.get(i).generateOps(records, isPop);
                }
                //清空集合
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            return didSomething;
        }
    

    generateOps方法点进入,发现是OpGenerator接口的一个抽象方法,它的实现其中一个是在BackStackRecord中,那就看一下,
    BackStackRecord#generateOps:

        @Override
        public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
            records.add(this);
            isRecordPop.add(false);
            if (mAddToBackStack) {
                //关键
                mManager.addBackStackState(this);
            }
            //始终返回true,上面的while条件一定成立
            return true;
        }
    

    步骤3中,mAddToBackStack被设为true,进入判断,执行 mManager.addBackStackState(this);回到FragmentManager中来:

       void addBackStackState(BackStackRecord state) {
            if (mBackStack == null) {
                mBackStack = new ArrayList<BackStackRecord>();
            }
            mBackStack.add(state);
        }
    

    至此,我们可以说,凡是调用了.addToBackStack(null)方法,都会将相关的BackStackRecord放入FragmentManager中的成员变量mBackStack集合中。

    generateOps方法这里始终返回true,那么就会执行while内的任务removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);,看名字是移除多余的操作并执行,然后经过一系列调用,最终执行的是record.executePopOps(moveToState);或者是record.executeOps()方法。先看record.executeOps():

    6、BackStackRecord#executeOps

    void executeOps() {
            final int numOps = mOps.size();
            for (int opNum = 0; opNum < numOps; opNum++) {
                final Op op = mOps.get(opNum);
                final Fragment f = op.fragment;
                if (f != null) {
                    f.setNextTransition(mTransition, mTransitionStyle);
                }
                switch (op.cmd) {
                    case OP_ADD:
                        f.setNextAnim(op.enterAnim);
                        mManager.addFragment(f, false);
                        break;
                    case OP_REMOVE:
                        f.setNextAnim(op.exitAnim);
                        mManager.removeFragment(f);
                        break;
                    case OP_HIDE:
                        f.setNextAnim(op.exitAnim);
                        mManager.hideFragment(f);
                        break;
                    case OP_SHOW:
                        f.setNextAnim(op.enterAnim);
                        mManager.showFragment(f);
                        break;
                    case OP_DETACH:
                        f.setNextAnim(op.exitAnim);
                        mManager.detachFragment(f);
                        break;
                    case OP_ATTACH:
                        f.setNextAnim(op.enterAnim);
                        mManager.attachFragment(f);
                        break;
                    case OP_SET_PRIMARY_NAV:
                        mManager.setPrimaryNavigationFragment(f);
                        break;
                    case OP_UNSET_PRIMARY_NAV:
                        mManager.setPrimaryNavigationFragment(null);
                        break;
                    default:
                        throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
                if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
                    mManager.moveFragmentToExpectedState(f);
                }
            }
            if (!mReorderingAllowed) {
                // Added fragments are added at the end to comply with prior behavior.
                mManager.moveToState(mManager.mCurState, true);
            }
        }
    

    遍历BackStackRecord对象中的mOps 集合,swith到mOps对象封装的任务,最终调用FragmentManager的相关方法执行该任务。

    相关文章

      网友评论

          本文标题:Fragment相关

          本文链接:https://www.haomeiwen.com/subject/lzoqsftx.html