从源码角度理解 FragmentTransaction实现

谈到fragment的使用,肯定绕不过FragmentTransaction事务,对fragment的操作必定用到它,其提供show,hide,add,remove,replace等常用的fragment操作,最后commit操作,这么强大的管理类,它内部是如何实现的呢?为什么可以连续调用多个api,最后一次要commit操作?

1、创建FragmentTransaction对象:

 FragmentTransaction ft = mFragmentManager.beginTransaction();

源码实现:

 @Override
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }

可以看到new 出一个新的 BackStackRecord对象,这个BackStackRecord就是api的真正实现者。

我以remove fragment这个操作看下内部实现:


    @Override
    public FragmentTransaction remove(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_REMOVE;
        op.fragment = fragment;
        addOp(op);
        return this;
    }

这个Op是什么东东呢?,又把op加入到addOp中,我们不妨先看下这个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++;
    }

这段代码还是比较容易理解的,其实remove操作,就是新建一个 链表结点,结点中保存了当前的操作,保存了fragment的对象引用,所以 FragmentTransaction的内部主要实现就是通过链表操作的,每个链表结点保存了每一个api操作的信息,好了,我们看下commit的实现:

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

只能commit一次,否则抛出异常

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

看下 mManager.enqueueAction(this, allowStateLoss)的实现:

    /**
     * Adds an action to the queue of pending actions.
     *
     * @param action the action to add
     * @param allowStateLoss whether to allow loss of state information
     * @throws IllegalStateException if the activity has been destroyed
     */
    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().post(mExecCommit);
            }
        }
    }

代码解读:
1、先去检查 checkStateLoss 状态,正常commit操作,是肯定去检查的,如果是在onSaveInstanceState状态保存之后,再去commit操作,肯定会报错。

2、BackStackRecord 类实现了 Runnable,看其run方法。

     public void run() {
        // 省略部分代码
        // 指向链表头指针
        Op op = mHead;
        // 遍历链表结点
        while (op != null) {
            //每一个结点的cmd操作
            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);
        // 是否调用了addBackStack(),如果加入,将当前的BackStackRecord加入到栈中
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }

这段代码解读:

1、每次commit之后,都会commit一个runnable任务,run()方法里面 对链表进行遍历操作,从头结点开始,依次访问每个结点,然后读取里面每一个结点的cmd,执行相应的方法。注意replace,是先remove,后add。
2、如果调用了addBackStack()方法,会将当前的任务对象加入到栈中。

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

可见我的这篇博客 从源码角度解释 fragment 坑(一)

我在想,FragmentTransaction 事务内部为什么要实现链表操作呢?

如果单纯的只是commit之前调用几个api,hide,show我认为可能没有必要,它的必要性,就是在退栈的时候,能够记住你之前的每个结点的行为,并且进行相应的反操作,比如我 先用事务,hide 1,remove 2,add 3,甚至更多,退栈的时候,我们是不是先执行,remove 3,往往3走到onDestroyview方法,2 调用 onCreateView,show 1出来,如果不用链表的操作的话,确实不太方便。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值