[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]
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源代碼分析(三) 事務