1. 程式人生 > >[Android]Fragment源代碼分析(三) 事務

[Android]Fragment源代碼分析(三) 事務

gin == ted n) 源代碼 actions because comm 承擔

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管理的部分源代碼。





[Android]Fragment源代碼分析(三) 事務