[Android]Fragment原始碼分析(三) 事務

來源:互聯網
上載者:User

標籤:批量   action   java   instance   tar   containe   bre   zed   tee   

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管理的部分原始碼。





[Android]Fragment原始碼分析(三) 事務

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.