Android 元件之FragmentManager與FragmentTransaction小述
一、概述
本節主要分析下FragmentManager與FragmentTransaction的內部程式碼,瞭解一下當我們提交事務時,兩者是怎麼協調處理的
二、FragmentManager的介紹
關於FragmentManager的內容我們可以檢視官網FragmentManager,當然更詳細的我們也可以檢視FragmentManager原始碼,在這裡我就簡單的介紹下,當我們在Activity中要獲取FragmentManager時,我們直接呼叫的getFragmentManager()方法
FragmentManager fm = getFragmentManager ();
如果你只想使用FragmentManager,那麼這樣一行程式碼就完全足夠了,但是如果你想知其所以然,那麼有一些地方就需要注意了,因為FragmentManager是一個抽象類,所以我們獲取到的只是FragmentManager的一個實現類(子類FragmentManagerImpl)物件而已,具體的獲取過程我們可以接著往下看,這是我們超類Activity類的部分程式碼
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
這是我們FragmentController類,我們發現其又呼叫了mHost.getFragmentManagerImpl()方法
private final FragmentHostCallback<?> mHost;
public static final FragmentController createController (FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}
public FragmentManager getFragmentManager() {
return mHost.getFragmentManagerImpl();
}
這是我們的抽象類FragmentHostCallback< E >,通過物件組合,最後呼叫方法的返回值即一個FragmentManagerImpl物件
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}
而FragmentManagerImpl又何許東西也?這就是我們最後獲取到的FragmentManagerImpl類,是一個內部類
public abstract class FragmentManager {
...//省略程式碼
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
...
}
...
}
知道了這些我們就明白了,其實我們平時所使用的findFragmentById都是FragmentManagerImpl的方法,多型嘛
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
/**注意只是根據需要展示了FragmentManagerImpl的部分程式碼*/
ArrayList<Fragment> mAdded;//管理著一個fragment佇列
ArrayList<BackStackRecord> mBackStack;//管理著一個回退棧
@Override public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
//addFragment、removeFragment、hideFragment、showFragment
//detachFragment、attachFragment常用方法
public void addFragment(Fragment fragment, boolean moveToStateNow) {
//將fragment新增到容器裡,並改變相應的標誌位
...
}
//findFragmentById、findFragmentByTag常用方法
public Fragment findFragmentById(int id) {
//從容器里根據id取出相應的fragment
...
}
在這裡我只是簡單展示了一下FragmentManagerImpl的極少量程式碼,讓我們心裡先有個大致印象,當我們介紹事務FragmentTransaction的時候,我們會接觸到FragmentManagerImpl更多的方法,明白當我們提交一個事務時,兩者是怎麼協調處理的。
三、FragmentTransaction的分析
上面我們說了FragmentManager,通過beginTransaction()方法,我們便能開啟一個事務,之後就是我們的一些普通操作了
private void addFragment4() {
FragmentManager fm = getFragmentManager();
//在這裡為了直觀展示,就加一個區域性變數
FragmentTransaction fragmentTransaction = fm.beginTransaction();
MyFragment4 fragment4 = (MyFragment4) fm.findFragmentById(R.id.activity5_fragment);
if(fragment4 == null){
fragment4 = new MyFragment4();
fragmentTransaction.add(R.id.activity5_fragment, fragment4)
//根據需要是否要加入到回退棧
.addToBackStack(null)
.commit();
}
}
我們知道,fm.beginTransaction()實際呼叫的是其子類FragmentManagerImpl的方法,其實就是個多型思想
@Override public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
通過官網FragmentTransaction我們可以知道,FragmentTransaction也是一個抽象類,我們所使用的事務即其子類BackStackRecord類,類中程式碼非常多,我們可以檢視BackStackRecord原始碼,在這裡,我就根據我們常用的add並commit的流程來簡單介紹下BackStackRecord,並瞭解一下其與FragmentManagerImpl是怎麼協調工作的,這是我們的BackStackRecord類
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, Runnable {
//在這裡我們持有FragmentManagerImpl物件
//當然也就能根據需要呼叫FragmentManagerImpl方法了
final FragmentManagerImpl mManager;
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
}
這是我們的FragmentManagerImpl類,當我們在Activity中beginTransaction()時,我們知道我們建立了一個事務BackStackRecord,並將自身引用傳遞了過去
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
/**注意只是根據需要展示了FragmentManagerImpl的部分程式碼*/
ArrayList<Fragment> mAdded;//管理著一個fragment佇列
@Override public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
}
之後我們會呼叫BackStackRecord類的add(int var1, Fragment var2)方法
public FragmentTransaction add(int containerViewId, Fragment fragment) {
//doAddOp(...)這是一個私有方法
//在方法裡會建立一個Op物件
//Op物件的作用就是記錄一次操作的動作和Fragment引用以及操作使用的動畫
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
之後我們接著呼叫BackStackRecord類的addToBackStack(null)加入回退棧方法
public FragmentTransaction addToBackStack(String name) {
if (!mAllowAddToBackStack) {
throw new IllegalStateException("This FragmentTransaction is not allowed to be added to the back stack.")
}
//我們發現其實這個方法只是改變了標誌位,並沒有實際的邏輯程式碼
//這也是我們為什麼必須在commit之前加入回退棧的原因
//因為commit之後加入,沒有改變標誌位的事務就不會被提交
mAddToBackStack = true;
mName = name;
return this;
}
之後我們接著在BackStackRecord類中呼叫commit()方法,提交我們的本次的事務
public int commit() {
return commitInternal(false);
}
//如果提交時,生命週期處於Saving Activity之後
//那麼使用commit就會由於丟失資訊從而丟擲錯誤
//如果不需要儲存資訊,可以使用commitAllowingStateLoss
public int commitAllowingStateLoss() {
return commitInternal(true);
}
//在這裡我們就簡單看下allowStateLoss為false的情況
int commitInternal(boolean allowStateLoss) {
...//省略部分程式碼
if (mAddToBackStack) {
//使用mAvailBackStackIndices和mBackStackIndices兩個陣列
//來為BackStackRecord分配Index
mIndex = mManager.allocBackStackIndex(this);
} else {
Index = -1;
}
//這是我們最後提交的方法,會呼叫FragmentManagerImpl類的enqueueAction方法
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
那我們接下來就看下FragmentManagerImpl類的allocBackStackIndex(…)方法,當加入返回棧並在commit事務之前
// Must be accessed while locked.
ArrayList<BackStackRecord> mBackStackIndices;
ArrayList<Integer> mAvailBackStackIndices;
public int allocBackStackIndex(BackStackRecord bse) {
synchronized (this) {
if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
if (mBackStackIndices == null) {
mBackStackIndices = new ArrayList<BackStackRecord>();
}
int index = mBackStackIndices.size();
mBackStackIndices.add(bse);
return index;
} else {
int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
mBackStackIndices.set(index, bse);
return index;
}
}
}
這是我們最後commit提交後呼叫的FragmentManagerImpl類的enqueueAction(…)方法
ArrayList<Runnable> mPendingActions;//每提交一個事務都在不同執行緒裡
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
//這個方法我就不細寫了,其主要作用
//遍歷mPendingActions管理的事務執行緒
//並呼叫每個執行緒事務(BackStackRecord)類的run方法
//隨後一個個移除相應的事務執行緒
execPendingActions();
}
};
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
//這是管理事務的執行緒集合,每個執行緒即代表著一個待處理的事務操作
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
//mHost.getHandler()返回一個handler物件
//即handler.post(Runnable action)
//所以會呼叫mExecCommit的run方法
mHost.getHandler().post(mExecCommit);
}
}
}
這就是我們的BackStackRecord類的run方法,在這裡我們根據Op物件所攜帶的資訊,判斷進行哪種操作,隨後再呼叫FragmentManagerImpl的方法,moveToState(…)和是否需要將事務新增到回退棧
public void run() {
...
//根據OP的資訊呼叫相應的操作,例如當我們add操作
switch (op.cmd) {
case OP_ADD:
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
//將當前操作的fragment新增到FragmentManager管理的列表中
mManager.addFragment(f, false);
break;
case OP_REMOVE:
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.removeFragment(f, mTransition, mTransitionStyle);
break;
...
}
...
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
if (mAddToBackStack) {
//將本次事務新增到回退棧
mManager.addBackStackState(this);
}
}
那麼最後我們再接著回來看下FragmentManagerImpl類的部分方法
public void addFragment(Fragment fragment, boolean moveToStateNow) {
//將fragment新增到管理佇列中,並改變相應的標誌位
...
}
//在回退棧中新增一個事務BackStackRecord
void addBackStackState(BackStackRecord state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
//回撥onBackStackChanged
reportBackStackChanged();
}
//這個方法非常重要,每當Fragment的生命週期中狀態改變,都會被呼叫
//這也保證了Fragment與Activity能夠保持同步
//具體的程式碼,感興趣的可以檢視原始碼瞭解下
void moveToState(int newState, int transit, int transitStyle, boolean always) {
//Fragment的狀態切換的操作邏輯
...
}
到這裡分析就已經結束了,開啟並提交一個事務,是否將事務新增到返回棧兩種情況,我們合二為一的做了介紹,下面我們再來看一下,當我們按返回鍵時,當有事務在返回棧的情況下,事務是怎麼被彈出棧的,這是我們Activity的程式碼
public void onBackPressed() {
if (mActionBar != null && mActionBar.collapseActionView()) {
return;
}
//如果我們Fragment的事務回退棧還有事務能被彈出則返回true,否則返回false
if (!mFragments.getFragmentManager().popBackStackImmediate()) {
//經過一些其他條件的判斷,最終會呼叫finish()方法
finishAfterTransition();
}
}
下面我們來接著看下FragmentManagerImpl類的popBackStackImmediate(…)方法
@Override public boolean popBackStackImmediate() {
checkStateLoss();
//如果存在待處理的事務,則直接返回true
executePendingTransactions();
//引數是固定的,註釋看下面
return popBackStackState(mHost.getHandler(), null, -1, 0);
}
這是FragmentManagerImpl類的popBackStackState(…)方法
//1往左移0位,實際還是1,並且0&1=0 (按位與)
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
boolean popBackStackState(Handler handler, String name, int id, int flags) {
//如果我們的回退棧為null,那麼直接返回false退出Activity
if (mBackStack == null) {
return false;
}
//當返回鍵的時候,傳參是固定的,所以直接走這個分支
if(if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0)) {
int last = mBackStack.size()-1;
if (last < 0) {
return false;
}
//將我們管理的回退棧最頂層的remove掉
final BackStackRecord bss = mBackStack.remove(last);
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
//作用是迴圈所有Op,找到第一個被刪除的fragment
//和最後一個被新增的fragment
bss.calculateBackFragments(firstOutFragments, lastInFragments);
//這是主要方法,註釋看下面
bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
//FragmentManagerImpl管理著一個ArrayList<OnBackStackChangedListener> 集合
//這個方法作用便是迴圈遍歷onBackStackChanged()方法
reportBackStackChanged();
}else {
...
}
}
這是我們需要的BackStackRecord類的popFromBackStack(…)方法
public TransitionState popFromBackStack(boolean doStateMove, TransitionState state
, SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
...
//根據事務的Op攜帶的資訊,知道事務回退棧裡的事務是什麼操作,例如add
//知道棧裡的事務是的型別了,當然back時候就知道怎麼相應處理了
//例如事務A是add而來,那麼back後執行remove;
//如果事務A是replace而來,那麼back後執行先remove掉新的後再add進替換掉那個old的
Op op = mTail;
while (op != null) {
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = op.popExitAnim;
//因為回退棧裡的事務裡Op標識說明這是新增操作
//所以本次back鍵直接將這個事務removeFragment即可
mManager.removeFragment(f
, FragmentManagerImpl.reverseTransit(mTransition)
, mTransitionStyle);
}
break;
...//其他操作情況
}
}
...
}
四、總結
本節主要簡單分析了一下我們在新增事務時的具體實現流程,如果想要檢視更多Fragment的基礎知識,去我的部落格目錄裡檢視吧,因為關於每塊知識點的介紹,部落格單節寫的比較零散,不容易查詢