zoukankan      html  css  js  c++  java
  • [Android]Fragment源代码分析(三) 事务

    Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩。我们先引入一个简单经常使用的Fragment事务管理代码片段:

                FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
                ft.add(R.id.fragmentContainer, fragment, "tag");
                ft.addToBackStack("<span style="font-family: Arial, Helvetica, sans-serif;">tag</span><span style="font-family: Arial, Helvetica, sans-serif;">");</span>
                ft.commitAllowingStateLoss();

    这段代码运行过后,就能够往fragmentContainer控件中增加Fragment的内部持有控件。

    上一讲我们说到Fragment通过状态机的变更来生成内部的mView。当你使用的是非from layout.xml方式的时候,它会在Fragment.CREATED状态下搜索container相应的控件然后将mView增加到这个父控件中。

    那么这个事务又在这里面承担什么样的作用呢?

    我们先来看Manager.beginTransaction这种方法的返回值:

    /**
         * Start a series of edit operations on the Fragments associated with this
         * FragmentManager.
         *
         * <p>
         * Note: A fragment transaction can only be created/committed prior to an
         * activity saving its state. If you try to commit a transaction after
         * {@link FragmentActivity#onSaveInstanceState
         * FragmentActivity.onSaveInstanceState()} (and prior to a following
         * {@link FragmentActivity#onStart FragmentActivity.onStart} or
         * {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will
         * get an error. This is because the framework takes care of saving your
         * current fragments in the state, and if changes are made after the state
         * is saved then they will be lost.
         * </p>
         */
        public abstract FragmentTransaction beginTransaction();
    在Fragment的管理中FragmentManager的实现类是FragmentManagerImpl,当然这也是Android一贯的命名方式;

    FragmentManagerImpl.java:
    @Override
        public FragmentTransaction beginTransaction() {
            return new BackStackRecord(this);
        }

    FragmentManager通过返回一个叫做BackStackRecord的对象完毕事务处理。抛开Android本身,我们对于事务的理解主要源于数据库,也就是一种批量性的操作,记录下你的操作集合,然后一次性处理,保证事务处理时候的安全性和高效性。FragmentManager看待事务的观点也基本一致,BackStackRecord的核心方法是addOp(Op):

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

    我们看到,对于BackStackRecord对操作处理的组织是採用"迭代器"的模式,每个操作被记录成为Op对象,又能够当作"备忘录"模式来看待。而对于加入操作的入口:

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

    是採用"Builder"的方式来组织。

    文章開始我已经提到了,Fragment的事务管理是比較出彩的代码,单纯的事务採用了至少三套模式来组织,并且组织起来丝毫没有感觉。

    当然Fragment带给我们的惊喜还不仅限于此。我们总上面的代码片段能够看出,实际上,通过事务类BackStackRecord生成Op对象实际上在复制BackStackRecord的属性,所以当我们分析每个Op里面的数据的时候,能够直接用BackStackRecord中的属性映射。

        int mNumOp;//Op数量
        int mEnterAnim;//进入动画
        int mExitAnim;//退出动画
        int mPopEnterAnim;//弹出进入动画
        int mPopExitAnim;//弹出退出动画
        int mTransition;//转场动画
        int mTransitionStyle;
        boolean mAddToBackStack;//是否增加到BackStack中

    Op本身属于Command模式,它的Command列表各自是:

        static final int OP_NULL = 0;
        static final int OP_ADD = 1;
        static final int OP_REPLACE = 2;
        static final int OP_REMOVE = 3;
        static final int OP_HIDE = 4;
        static final int OP_SHOW = 5;
        static final int OP_DETACH = 6;
        static final int OP_ATTACH = 7;

    也许你也已经看出来了,没错,Op的属性就是作为Command的操作数。在BackStackRecord进行Commit了之后,BackStackRecord会将自己纳入FragmentManagerImpl的命令队列中处理。

    每个处理单元用于处理各自的Op操作。

    我们来看下代码:

    BackStackRecord.java:
    int commitInternal(boolean allowStateLoss) {
            if (mCommitted) throw new IllegalStateException("commit already called");
            mCommitted = true;
            if (mAddToBackStack) {
                mIndex = mManager.allocBackStackIndex(this);
            } else {
                mIndex = -1;
            }
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }

    BackStackRecord在提交的时候会将自己提交到mManager的Action队列中去。

    而这样的Action队列的处理能够在随意线程中进行

    FragmentManager.java:
     
    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);
                }
            }
        }

    我们看到实际上Action是被mExecCommit命令所运行,而它,将回调你所提交的BackStackRecord的run方法。

    BackStackRecord.java:
           Op op = mHead;
            while (op != null) {
                 ...
            }

    我们看到,命令是以迭代器的方式在循环执行。

    不同的命令将对Fragment有不同的状态变更操作,举个简单的样例:

    Op.java:
    
    case OP_ADD: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    } break;

    当我们须要Add一个Fragment的时候,Op将调用FragmentManager的addFragment方法

    FragmentManager.java:
    public void addFragment(Fragment fragment, boolean moveToStateNow) {
            if (mAdded == null) {
                mAdded = new ArrayList<Fragment>();
            }
            makeActive(fragment);
            if (!fragment.mDetached) {
                if (mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment already added: "
                            + fragment);
                }
                mAdded.add(fragment);
                fragment.mAdded = true;
                fragment.mRemoving = false;
                if (fragment.mHasMenu && fragment.mMenuVisible) {
                    mNeedMenuInvalidate = true;
                }
                if (moveToStateNow) {
                    moveToState(fragment);
                }
            }
        }

    FragmentManager会将它先增加到自己的mAdded队列中去,然后通过调用moveToState方法来改变Fragment状态保证状态上的一致性。而这一部分,就是我们上一部分讲的内容,我们不再赘述。这样Fragment通过这样的优雅的方式就实现了事务的处理。

    下一篇,我将给大家讲述Fragment关于Stack管理的部分源代码。





  • 相关阅读:
    Apache 虚拟主机 VirtualHost 配置
    EAX、ECX、EDX、EBX寄存器的作用
    Python中文文档 目录(转载)
    八度
    POJ 3268 Silver Cow Party (最短路)
    POJ 2253 Frogger (求每条路径中最大值的最小值,Dijkstra变形)
    2013金山西山居创意游戏程序挑战赛——复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560 我是歌手
    HDU 4549 M斐波那契数列(矩阵快速幂+欧拉定理)
    UVA 11624 Fire! (简单图论基础)
    HDU 3534 Tree (树形DP)
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7105766.html
Copyright © 2011-2022 走看看