標籤:批量 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原始碼分析(三) 事務