当前位置: 代码迷 >> Android >> [Android]Fragment源码分析(3) 事务
  详细解决方案

[Android]Fragment源码分析(3) 事务

热度:236   发布时间:2016-04-28 04:23:28.0
[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     * [email protected] FragmentActivity#onSaveInstanceState     * FragmentActivity.onSaveInstanceState()} (and prior to a following     * [email protected] FragmentActivity#onStart FragmentActivity.onStart} or     * [email protected] 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管理的部分源码。





  相关解决方案