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

來源:互聯網
上載者:User

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

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,然後在布局的時改觀現與自己想要的差太多了,與activity裡布效果差太多

這主要是因為手機的解析度不一樣導致的,你的模擬器和真實的手機解析度有差距,如果想解決的話可以在資源檔下寫幾分不同解析度的布局
 
拿到Android 項目源碼後,怎才可以以最高效的速度看懂?

1.顯然Eclipse不是閱讀Android源碼的好工具,不流暢,搜尋低效,繼承性關係/調用關係都無法有效查看。推薦Source Insight,在這個工具協助下,你才可以駕馭巨大數量的Android 源碼,你可以從容在Java,C++,C代碼間遨遊,你可以很快找到你需要的繼承關係。
順便,我們公司一直是Windows+Linux+Samba的工作模式。
2.宏觀上看,Android源碼分為功能實現上的縱向,和功能拓展上的橫向。在閱讀源碼時需要把握好著兩個思路。
譬如你需要研究音頻系統的實現原理,縱向:你需要從一個音樂的開始播放追蹤,一路下來,你發現Jni調用解碼庫,共用記憶體的建立和使用,路由的切換,音訊輸入裝置的開啟,音頻流的開始。
3.Android的功能模組絕大部分是C/S架構,你心裡一定需要有這個層級關係,你需要思路清晰地找到Server的位置,它才是你需要攻破的城。然後你才能發現HAL和Kernel一層層地剝離。
我大概在三個月前閱讀完Android UI系統的源碼,這是Android最複雜的部分,沒有之一。
我需要先找一個開頭,和UI有直接關係的就是最常見的Activity了吧,我就從它開始解剖。
我從Activity的建立入手,尋找Activity真正的建立位置,setContentview這個方法很明顯和UI有關,這兩方面一結合,我發現了ViewRoot和WindowManager的身影,沿著WM和WMS我發現了Surface,以及draw的函數,它居然在Activity 建立時出現的DeCorView上畫東西。藉助Source Insight我總結了UI Java層的橫向靜態圖。
完成這個靜態UML,我覺得我可以開始功能實現上追蹤了,這部分主要是C++的代碼(這也是我堅定勸阻的放棄Eclipse的原因),我沿著draw函數,看到了各個層級的關係,SurfaceSession的控制和交易處理,SharedBuffer讀寫控制,彪悍的SurfaceFlinger主宰一切,OpenGL ES的神筆馬良。FrameBuffer和FrameBufferDevice的映像輸出。一氣呵成的完成了。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.