Fragment系列之BackStackRecord源码

1. 简介:
本文章基于Fragment管理源码简介,对BackStackRecord的相关细节技术实现做详细的解读。

2. 主要的操作方法

从源码可以看到BackStackRecord中对Fragment的操作方法都是对FragmentTransaction的实现,主要的方法包括:

add、replace、remove、hide、show、detach、attach、addToBackStack、commit/ commitAllowingStateLoss,这是开发过程中常用到的几个方法,当然FragmentTransaction还有好多其它的方法可以自己去看源码。

3. 操作Op对象的命令:

OP_NULL、OP_ADD、OP_REPLACE、OP_REMOVE、OP_HIDE、OP_SHOW、OP_DETACH、OP_ATTACH,对应的都是整型值,其实可以看出和上面的操作方法时一一对应的。

4. 操作Op对象

//Op对象是一个双向链表结构

static final class Op {

        Op next;//上一个操作

        Op prev;//下一个操作

        int cmd;//操作命令对应上面3中的命令

        Fragment fragment;//操作的fragment

        int enterAnim;//进入动画

        int exitAnim;//退出动画

        int popEnterAnim;//pop入栈动画

        int popExitAnim;//pop出栈动画

        ArrayList<Fragment> removed;//这个对象用于replace时,保存当前contianerId上所有的被替换的fragment,这里缓存下来用于回退栈时用。

    }

5. run方法

在Fragment管理简介文章中说过,BackStackRecord实现了Runnable接口,并且在FragmentManager中会执行这个run方法,所以这个run方法是fragment操作的开始。

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

 

        if (mManager.mCurState >= Fragment.CREATED) {

            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();

            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();

            calculateFragments(firstOutFragments, lastInFragments);

            beginTransition(firstOutFragments, lastInFragments, false);

        }

 

        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;

                    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 = 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);

                                }

                            }

                        }

                    }

                    if (f != null) {

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

        }

    }

主要看一下while循环对op链表遍历并执行相关FragmentManager操作的地方,run方法中的操纵是正向操作(即按照BackStackRecord的操作顺序),可以看出和FragmentManager的对应关系:

OP_ADD:FM.addFragment

OP_REPLACE: FM.removeFragment(相同containerId的fragment);FM.addFragment(新添加的fragment)

OP_REMOVE:FM.removeFragment

OP_HIDE:FM.hideFragment

OP_SHOW:FM.showFragment

OP_DETACH:FM.detachFragment

OP_ATTACH:FM.attachFragment

 

6. popFromBackStack方法

上面的run方法是BackStackRecord的正向操作,popFromBackStack则是反向操作。当BackStackRecord被压栈,则pop时会对BackStackRecord的操作进行反向的操作,简单的说就是页面返回操作。

public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,

            SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {

        if (FragmentManagerImpl.DEBUG) {

            Log.v(TAG, "popFromBackStack: " + this);

            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);

            PrintWriter pw = new FastPrintWriter(logw, false, 1024);

            dump("  ", null, pw, null);

            pw.flush();

        }

 

        if (mManager.mCurState >= Fragment.CREATED) {

            if (state == null) {

                if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {

                    state = beginTransition(firstOutFragments, lastInFragments, true);

                }

            } else if (!doStateMove) {

                setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);

            }

        }

 

        bumpBackStackNesting(-1);

 

        Op op = mTail;

        while (op != null) {

            switch (op.cmd) {

                case OP_ADD: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popExitAnim;

                    mManager.removeFragment(f,

                            FragmentManagerImpl.reverseTransit(mTransition),

                            mTransitionStyle);

                }

                break;

                case OP_REPLACE: {

                    Fragment f = op.fragment;

                    if (f != null) {

                        f.mNextAnim = op.popExitAnim;

                        mManager.removeFragment(f,

                                FragmentManagerImpl.reverseTransit(mTransition),

                                mTransitionStyle);

                    }

                    if (op.removed != null) {

                        for (int i = 0; i < op.removed.size(); i++) {

                            Fragment old = op.removed.get(i);

                            old.mNextAnim = op.popEnterAnim;

                            mManager.addFragment(old, false);

                        }

                    }

                }

                break;

                case OP_REMOVE: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popEnterAnim;

                    mManager.addFragment(f, false);

                }

                break;

                case OP_HIDE: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popEnterAnim;

                    mManager.showFragment(f,

                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);

                }

                break;

                case OP_SHOW: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popExitAnim;

                    mManager.hideFragment(f,

                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);

                }

                break;

                case OP_DETACH: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popEnterAnim;

                    mManager.attachFragment(f,

                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);

                }

                break;

                case OP_ATTACH: {

                    Fragment f = op.fragment;

                    f.mNextAnim = op.popExitAnim;

                    mManager.detachFragment(f,

                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);

                }

                break;

                default: {

                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);

                }

            }

 

            op = op.prev;

        }

 

        if (doStateMove) {

            mManager.moveToState(mManager.mCurState,

                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);

            state = null;

        }

 

        if (mIndex >= 0) {

            mManager.freeBackStackIndex(mIndex);

            mIndex = -1;

        }

        return state;

    }

主要看一下while循环对op的反向操作,和FragmentManager对应的关系为:

OP_ADD:FM.removeFragment

OP_REPLACE: FM.removeFragment(当前OP的fragment);FM.addFragment(OP中revomed的fragments即之前删除的相同containerId的所有fragments)

OP_REMOVE:FM.addFragment

OP_HIDE:FM.showFragment

OP_SHOW:FM.hideFragment

OP_DETACH:FM.attachFragment

OP_ATTACH:FM.detachFragment

 

7. 计算FirstOutFragment和LastInFragment

在BackStackRecord中还有几个方法比较值得关注,他们时用于计算当前BackStackRecord中第一个出栈的Fragment以及最后一个添加的Fragment,即setFirstOut和setLastIn以及calculateFragments和calculateBackFragments。

calculateFragments和calculateBackFragments方法的实现和run与popFromBackStack实现有点类似,不同的时对应的操作命令调用的时setFirstOut和setLastIn,具体代码这里就不贴出来了,可以看源代码,这里列出对应的关系:

calculateFragments方法是正向的:

OP_ADD:setLastIn(统计最后一个加入的Fragment)

OP_REPLACE: setFirstOut(将之前添加在同一个containerId的fragment都添加到出栈列表中);setLastInOP_REMOVE:setFirstOut

OP_HIDE:setFirstOut

OP_SHOW:setLastIn

OP_DETACH:setFirstOut

OP_ATTACH:setLastIn

以上是正向的操作关系;

 

calculateBackFragments方法是方向的:

OP_ADD:setFirstOut

OP_REPLACE: setLastIn(将之前添加在同一个containerId的fragment都添加到入栈列表中);setFirstOut

OP_REMOVE:setLastIn

OP_HIDE:setLastIn

OP_SHOW:setFirstOut

OP_DETACH:setLastIn

OP_ATTACH:setFirstOut

以上是反向的操作关系。

一个containerId只关联一个firstOutFragment和一个lastInFragment对象。

那么这两个方法计算出FirstOutFragment和LastInFragment的意义何在呢?其实是用于fragment进入和出入时动画效果的。//todo 具体的动画机制还没弄清楚

8. BackStackState对象

在BackStackRecord类文件中还有一个BackStackState类,从名字上看是用于保存BackStackRecord状态的,具体的作用没有做太多深入,但其定义不是太复杂,从定义上看它的作用就是可以被序列化传输,然后可以通过其中的状态值实例化一个BackStackRecord对象,那么其实作用就是为BackStackRecord序列化传输用的。

 

  1. 总结

以上就是对BackStackRcord常用的细节点的解读,可以配合源码看一遍,好多人可能会在正向和反向对Op对象遍历的地方有点迷糊,这里需要弄清楚了然后在FragmentManager的moveToState方法时才能更好的理解为什么这么设计以及FragmentManager是如何切换Fragment状态的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值