自動Android在3.0版本中退出Fragment以來,fragment在我們日常的開發中無處不在,他使我們的在開發android時能更好的做到view的解耦。關于Fragment的用法,相信大家已經用的滾瓜爛熟了,各種FragmentTransaction的操作,都信手拈來。今天我們要從源碼的角度去剖析fragment內部實現的原理,我相信只有了解了內部實現原理,我們在碰到fragment的issue的時候才知道如何去解決。
我們今天要分析的是support v4包中的Fragment,相信絕大部分人都是用的兼容包中的fragment。
先上一張圖:
這張圖描述的是與fragment相關的類的UML圖,基本描述了這些類之間的關系。
Fragment理論上可以被任何對象持有,然后管理其生命周期,但是絕大部分時候,我們都是在activity里面使用它,我們可以從Activity出發,理清楚這些類之間的關系。
FragmentActivity
兼容包中,支持fragment的activity叫做FragmentActivity, 我們常用的AppCompatActivity也是繼承自它,
在FragmentActivity中有一個重要的成員變量mFragments,它的類型是FragmentController
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
正是這個mFragments的存在使得在FragmentActivity中進行fragment操作成為可能,我們可以看到在FragmentActivity中進行的任何fragment的操作都得經FragmentController之手
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
//...
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
//...
mFragments.dispatchCreate();
}
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
因此,我們進到FragmentController類中去一探究竟。打開FragmentController,發現其函數實現非常簡單。基本上你可以認為FragmentController是FragmentManager的代理,Activity需要的FragmentController的函數,最后調用的都是FragmentHostCallback中的FragmentManager的方法。
FragmentHostCallback,我們可以認為他是用來管理Fragment生命周期,同時作為代理提供fragment需要與外界打交道時的函數實現的比如從fragment中啟動新的Activity, 請求權限等等。其Fragment生命周期的的管理由FragmentManager負責,其余部分代理功能由其自己負責。
我們打開FragmentHostCallback的源代碼,可以看到其頭頂注釋中寫著:fragment可以被任何對象持有,要使一個對象具有持有和管理fragment生命周期的能力,我們只需要實現FragmentHostCallback中的函數。顯然,我們最常見的FragmentActivity肯定實現了FragmentHostCallback,我們跳轉到FragmentActivity,看到其內部有一個非靜態內部類HostCallbacks, 正式這個內部類的存在,使得FragmentActivity具有了持有和管理Fragment的能力,Fragment與外部交互的功能都由FragmentActivity實現了。至于FragmentController,前面已經說到,只是作為中間橋梁的作用。
Fragment
前面講了這么多,我們還沒有開始介紹今天的主角Fragment, 接下來我們就來揭開其稍許神秘的面紗。
Fragment = Attr + View + + State, 此處等待UML圖
Attr
Attr指得是Fragment的一些固有屬性,不會隨著Fragment的生命周期發生變化的,比如
Bundle mArguments; //構造參數
boolean mFromLayout; //是否從layout文件中創建
...
View
View是Fragment創建出來并顯示給用戶的界面的view,如果Fragment被持有,會被添加到Activity某一塊layout中去
// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
// The View generated for this fragment.
View mView;
// The real inner view that will save/restore state.
View mInnerView;
State
State指的是Fragment在生命周期變遷中中會發生改變的狀態
int mState = INITIALIZING; //生命周期狀態
boolean mAdded; //是否被添加
boolean mRemoving; //是否被移除
boolean mHidden;// 是否被隱藏
boolean mDetached; //是否已經分離
...
其中Fragment的成員變量mState,直接映射了Fragment的生命周期狀態變遷,其取值狀態在以下常量中:
static final int INITIALIZING = 0; // Not yet created.
static final int CREATED = 1; // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3; // Fully created, not started.
static final int STARTED = 4; // Created and started, not resumed.
static final int RESUMED = 5; // Created started and resumed.
我們在開中經常重寫的onCreate, onResume, onStop方法都發生mState的變遷過程中。 接下來,我們就來看看Fragment的生命周期是如何變化的。
Fragment事務操作:BackStackRecord
我們在開發中,每次要操作那個Fragment的添加,刪除,隱藏,顯示等,都需要使用FragmentTransaction,比如添加一個Fragment:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.contaniner, testFragment);
transaction.commit();
FragmentTransaction實際上是一個抽象類,里面定義了一些關于Fragment操作的函數接口,
public abstract class FragmentTransaction {
add
replace
remove
hide
show
detach
attach
addToBackStack
...
}
從FragmentManger.beginTransaction真正返回的確是一個BackStackRecord類, 其實現了FragmentTransaction所定義的接口。
接下來,我們以添加為例,看看BackStackRecord是如何完成Add這個transaction的。
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
BackStackRecord中實現的四個add函數,最后都調用了doAddOp,我們注意到已經添加過的Fragment其tag和被添加到的containerId是不能更改的,否則會拋異常。 函數最后new了一個Op,Op是啥?Op其實就是Operation的簡稱,有經驗的讀者應該知道在一次FragmentTransaction中實際上可以進行多次add,remove之類的操作。每次操作,都會生成一個新的Op對象,在transaction commit操作時,會將這些Operation全部執行掉。
我們來看看Op類對象里都有些啥
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
從next, prev 兩個指針可以看出,Op是作為一個鏈表的節點而存在的,因此FragmentTransaction肯定是在一個鏈表中存儲了一次事務中的所有需要執行的操作。
cmd定義了Operation操作的類型
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;
fragment指定了這次Operation要操縱哪一個Fragment
此外Op類還包含了轉場動畫和一個操作會移除的Fragment集合。
doAddOp函數最后執行了
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++;
}
addOp執行了一個典型的添加節點到鏈表末尾的數據結構操作,并將transaction的動畫賦給op,最后講mNumOp加一,mNumOp代表了這次transaction中包含的操作個數。如果為0,則isEmpty返回true.
等到操作都添加好了之后,就差commit了。
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
if (mAddToBackStack) {
//添加到返回棧
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
如果設置了addToBackStack,就會執行添加到返回棧的操作,這個我們后面會專門講,mManager.enqueueAction 顧名思義就是講當前的transaction操做入隊列,因此在FragmentManager肯定會維護一個隊列來存儲當前尚未執行的transaction.
public void enqueueAction(Runnable action, boolean allowStateLoss) {
...
synchronized (this) {
...
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
enqueueAction首先會將action,添加到一個叫mPendingAction的列表中去,這個列表中存儲著一堆Runnable對象,代表著還未執行的事務,稍等一會兒,我們剛剛說到的是BackStackRecord類對吧,怎么可以添加到Runnable列表中呢,原來BackStackRecod本身還是實現了Runnabled接口,是一個可以執行的對象。添加完畢之后,會調用FragmentHostCallback中提供的getHandler方法,獲取到Handler方法,然后向主線程的MessageQueue中發送一個mExecCommit可執行對象
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
public boolean execPendingActions() {
boolean didSomething = false;
while (true) {
int numActions;
synchronized (this) {
if (mPendingActions == null || mPendingActions.size() == 0) {
break;
}
numActions = mPendingActions.size();
if (mTmpActions == null || mTmpActions.length < numActions) {
mTmpActions = new Runnable[numActions];
}
//講mPendingAction中的對象轉移到mTmpActions
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
}
//遍歷mTmpActions,執行run
mExecutingActions = true;
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}
...
return didSomething;
}
因此繞來繞去,最后調用的BackStackRecord本身的run方法,這也符合了commit方法名字本身的定義,transaction只是被批量提交到了主線程的任務隊列里,并不是馬上執行,等待主線程的looper去安排這些任務的執行。
那好,我們現在回家吧,去看看BackStackRecord。
public void run() {
...
Op op = mHead;
while (op != null) {
int enterAnim = state != null ? 0 : op.enterAnim;
int exitAnim = state != null ? 0 : op.exitAnim;
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break;
case OP_REPLACE: {
...
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.removeFragment(f, transition, transitionStyle);
} break;
case OP_HIDE: {
...
} break;
...
//更多case
}
op = op.next;
}
//將active狀態的fragment全部執行狀態變遷檢查
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
run方法內部根據不同的cmd走了很不不同的分支,每個分支內部都會對,fragment狀態做改變,最后調用moveToState將fragment的生命周期狀態mState進行變遷。
當然我們注意到hide和show,它們內部實際上不會調用調用moveToState, 因為hideFragment實際上就做了三件事請,
- 設置mHidden 為true
- fragment.mView.setVisibility(View.GONE); 隱藏fragment的view
- fragment.onHiddenChanged(true); 調用onHiddenChange
showFragment也是類似,只不過行為正好相反。
public void addFragment(Fragment fragment, boolean moveToStateNow) {
...
makeActive(fragment);
if (!fragment.mDetached) {
...
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
}
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
addFragment會將fragment添加到 mAdded和 mActive這兩個集合當中,這兩個集合維護了當前activity中維護的已經添加的fragment列表和當前處于活躍狀態的fragment列表,如果fragment位于mActive中,那么當activity的狀態發生變化時,fragment也會跟隨著發生變化。FragmentManger 如何引導fragment的狀態發生變化呢?
這一切都發生在moveToState函數當中
Fragment狀態變遷:moveToState
Fragment狀態變遷發生在用戶主動發起transaction,或者fragment被add到activity之后跟隨activity的生命周期變化一起發生改變。每次狀態變遷最終都會走到函數moveToState,字面意思是將fragment遷移到新的狀態
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// Fragments that are not currently added will sit in the onCreate() state.
...
if (f.mState < newState) {
// For fragments that are created from a layout, when restoring from
// state we don't want to allow them to be created until they are
// being reloaded from the layout.
...
switch (f.mState) {
case Fragment.INITIALIZING:
...
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
...
}
case Fragment.ACTIVITY_CREATED:
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.performResume();
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
}
} else if (f.mState > newState) {
switch (f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
f.performPause();
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
f.performStop();
}
case Fragment.STOPPED:
if (newState < Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
f.performReallyStop();
}
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
...
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
...
}
}
}
...
}
fragment的state取值,為前面提到的七中狀態,其中最低值是INITIALIZING狀態,代表fragment剛創建,還未被add, 最高狀態值是RESUMED,代表fragment處于前臺。 所以moveToState內部分兩條線,狀態躍升,和狀態降低,里面各有一個switch判斷,注意到switch里每個case都沒有break,這意味著,狀態可以持續變遷,比如從INITIALIZING,一直躍升到RESUMED,將每個case都走一遍,每次case語句內,都會改變state的值。
比如我們常見的add操作,最后調用的是
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
就是將fragment遷移到FragmentManager當前的狀態,因為我們不知道用戶什么時候add fragment,因此fragment被add之后,就將其狀態遷移到FragmentManager當前的狀態,然后跟隨FragmentManager一起發生狀態變遷,除非用戶手動removeFragment將其從mActive列表中移除。
public void dispatchCreate() {
mStateSaved = false;
moveToState(Fragment.CREATED, false);
}
public void dispatchActivityCreated() {
mStateSaved = false;
moveToState(Fragment.ACTIVITY_CREATED, false);
}
public void dispatchStart() {
mStateSaved = false;
moveToState(Fragment.STARTED, false);
}
public void dispatchResume() {
mStateSaved = false;
moveToState(Fragment.RESUMED, false);
}
public void dispatchPause() {
moveToState(Fragment.STARTED, false);
}
public void dispatchStop() {
// See saveAllState() for the explanation of this. We do this for
// all platform versions, to keep our behavior more consistent between
// them.
mStateSaved = true;
moveToState(Fragment.STOPPED, false);
}
public void dispatchReallyStop() {
moveToState(Fragment.ACTIVITY_CREATED, false);
}
public void dispatchDestroyView() {
moveToState(Fragment.CREATED, false);
}
public void dispatchDestroy() {
mDestroyed = true;
execPendingActions();
moveToState(Fragment.INITIALIZING, false);
mHost = null;
mContainer = null;
mParent = null;
}
這些 dispatchxxx函數由FragmentActivity狀態變化引發,然后調用moveToState將處于mActive集合中的fragment的狀態全部變更一次。
比如,當FragmentActivity pause的時候,其會通過FM
通知framgents進行狀態變遷。
/**
* Dispatch onPause() to fragments.
*/
@Override
protected void onPause() {
super.onPause();
mResumed = false;
if (mHandler.hasMessages(MSG_RESUME_PENDING)) {
mHandler.removeMessages(MSG_RESUME_PENDING);
onResumeFragments();
}
mFragments.dispatchPause();
}
Fragment狀態的保存
既然Fragment持有view,以及一些狀態屬性,那么在Activity保存自身狀態以便下次恢復的時候,就需要把fragment的狀態也保存起來,這樣activity被系統finish掉,然后重新創建時就能恢復上次的fragment狀態。那我們首先直奔FragmentActivity的onSaveInstanceState函數:
/**
* Save all appropriate fragment state.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//調用FragmentController 代理FragmentManager 保存狀態
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
//保存跟fragment啟動activity等待result的狀態
if (mPendingFragmentActivityResults.size() > 0) {
outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);
int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
}
outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
}
}
saveAllState函數就會將FragmentManager在重新恢復fragmentstate時需要的所有信息保存起來,小伙伴們可以跟進去看,我在這里畫了張圖:
關于返回棧的保存沒有詳細標注,不過我們后面會再詳細講到。
Fragment 狀態的恢復
Fragment狀態的恢復其實就是保存狀態的逆過程,不過,額外的工作是,我們需要根據之前保存的每個active的fragmentstate來恢復創建Fragment:
public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
if (mInstance != null) {
return mInstance;
}
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
//利用保存的狀態恢復fragment
mInstance = Fragment.instantiate(context, mClassName, mArguments);
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
return mInstance;
}
其中構造Fragment的代碼如下:
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
是不是發現這段代碼灰常熟悉,很多人恐怕都遇到過代碼中描述的異常。因為Fragment在恢復時,是利用反射的方式去創建,首先利用類加載器去加載類,然后調用其pubic empty 構造函數去創建fragment。
所以如果Fragment不是public的,或者fragment沒有public的無參構造函數,那么你的應用肯定會碰到這些異常,只不過你在調試的時候可能無法發現,因為你的手機環境太好,構不成saveInstanceState然后恢復它們的條件。
如果我們用Android studio 模板去創建fragment,那么他會給我們默認實現一個newInstance的靜態構造函數,并把構造參數寫在argument里,因為前面那張圖里可以看到,argument會在保存fragment狀態時保存起來的。另外不要寫任何帶參數的構造函數,因為這樣子,默認構造函數就會被隱藏,除非你手動去實現它。 話說這個instantiate是個public方法,因此你也可以直接調用這個函數去創建fragment,只不過它使用起來不是那么方便,使用者不知道我們需要往里面傳什么參數。
MyFragment.instantiate(activity, MyFragment.getClass().getName(), args)
臭名昭著的 “Can not perform this action after onSaveInstanceState”
這里不得不提一提開發中經常會碰到的一個異常,異常拋出時的message為:“Can not perform this action after onSaveInstanceState”, 意為,FragmentTransaction不能再onSaveInstanceState后提交,為什么會拋出這樣一個異常呢,因為FragmentManager認為在onSaveInstanceState 發生之后提交的transaction不能在下次Fm恢復時得到恢復,繼而認為這樣做是危險的,拒絕提交,除非你指定了allowStateLoss,即允許狀態的丟失。
開發者只需要用commitAllowingStateLoss,即可以成功提交這樣的transaction,即使它有可能會丟失狀態。
Fragment返回棧
使用fragmentTransaction的時候可以將其加入返回棧,這樣用戶就可以有機會去撤銷這一動作,將其從返回棧中pop出來。
public FragmentTransaction addToBackStack(String name) {
if (!mAllowAddToBackStack) {
throw new IllegalStateException(
"This FragmentTransaction is not allowed to be added to the back stack.");
}
//標記 即將加入返回棧
mAddToBackStack = true;
//標記在返回棧中的名字
mName = name;
return this;
}
//transaction提交
public void run() {
//...
//加入到FragmentManager的返回棧中
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
//FragmentManager添加返回棧
void addBackStackState(BackStackRecord state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
//通知返回棧監聽者們發生改變
reportBackStackChanged();
}
FragmentManager中有一個叫做mBackStack的列表,保存了添加到返回棧的所有record, 當用戶選擇popBackStack的時候,就可以將其pop出來。
我們常用的pop操作就是popBackStack函數,其實FM還提供了很多pop操作,它可以指定以name和id指定pop哪個record,還可以指定flag:POP_BACK_STACK_INCLUSIVE, 如果指定了這個flag就表示將name或者id相等且連續的record全部pop出來。這些操作最后都調用了函數
boolean popBackStackState(Handler handler, String name, int id, int flags) {
//如果返回棧不存在,立即返回
if (mBackStack == null) {
return false;
}
//如果name、id、POP_BACK_STACK_INCLUSIVE全部都沒有設置,直接移除棧頂的一個record
if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
int last = mBackStack.size()-1;
if (last < 0) {
return false;
}
final BackStackRecord bss = mBackStack.remove(last);
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
bss.calculateBackFragments(firstOutFragments, lastInFragments);
bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
reportBackStackChanged();
} else {
int index = -1;
if (name != null || id >= 0) {
//從棧頂開始尋找一個name或者id匹配的record,找到即跳出循環
index = mBackStack.size()-1;
while (index >= 0) {
BackStackRecord bss = mBackStack.get(index);
if (name != null && name.equals(bss.getName())) {
break;
}
if (id >= 0 && id == bss.mIndex) {
break;
}
index--;
}
//如果沒找到,函數返回
if (index < 0) {
return false;
}
//如果設置了POP_BACK_STACK_INCLUSIVE,則向棧底方向尋找連續匹配的record
if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
index--;
// Consume all following entries that match.
while (index >= 0) {
BackStackRecord bss = mBackStack.get(index);
if ((name != null && name.equals(bss.getName()))
|| (id >= 0 && id == bss.mIndex)) {
index--;
continue;
}
break;
}
}
}
if (index == mBackStack.size()-1) {
return false;
}
//彈出從棧頂到連續相同匹配的record記錄位置的所有record,加入集合states中
final ArrayList<BackStackRecord> states
= new ArrayList<BackStackRecord>();
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
final int LAST = states.size()-1;
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
//計算在連續record中,最先remove的fragment集合 和 最后進入的fragment集合
for (int i=0; i<=LAST; i++) {
states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
}
BackStackRecord.TransitionState state = null;
//對這些record列表執行逆操作
for (int i=0; i<=LAST; i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
state = states.get(i).popFromBackStack(i == LAST, state,
firstOutFragments, lastInFragments);
}
//通知返回棧監聽器更新
reportBackStackChanged();
}
return true;
}
其中比較復雜的兩步,一步是calculateBackFragments, 還有一個是popFromBackStack,我們先看calculateBackFragments:
為什么需要計算calculateBackFragments? 其實是因為涉及到動畫的方面,在一次在一次transaction中可能會移除和添加多次fragments,這樣動畫的編排就必須按順序來,這次分析中,我們就不對動畫做過多分析了,因為這是相對來說次要點的知識點。所以我們集中關心BackStackRecord 的popFromBackStack函數
public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
//...動畫相關
int transitionStyle = state != null ? 0 : mTransitionStyle;
int transition = state != null ? 0 : mTransition;
Op op = mTail;
while (op != null) {
int popEnterAnim = state != null ? 0 : op.popEnterAnim;
int popExitAnim= state != null ? 0 : op.popExitAnim;
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = popExitAnim;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
} break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (f != null) {
f.mNextAnim = popExitAnim;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
}
if (op.removed != null) {
for (int i=0; i<op.removed.size(); i++) {
Fragment old = op.removed.get(i);
old.mNextAnim = popEnterAnim;
mManager.addFragment(old, false);
}
}
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = popEnterAnim;
mManager.addFragment(f, false);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = popEnterAnim;
mManager.showFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = popExitAnim;
mManager.hideFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
} break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = popEnterAnim;
mManager.attachFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
} break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = popEnterAnim;
mManager.detachFragment(f,
FragmentManagerImpl.reverseTransit(transition), transitionStyle);
} break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
op = op.prev;
}
if (doStateMove) {
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(transition), transitionStyle, true);
state = null;
}
if (mIndex >= 0) {
mManager.freeBackStackIndex(mIndex);
mIndex = -1;
}
return state;
}
基本上從多個switch-case分支語句里就可以看出pop執行的就是commit的反操作,因為因為transaction都是成對存在的
- add <-> remove
- attach <-> detach
- show <-> hide
- replace( remove x n + add x m) <-> replace ( remove x m + add x n)
返回棧的狀態的保存
返回棧狀態的保存,相當于要把整個BackStackRecord的列表保存下來,以便下次恢復,前面提到BackStackRecord,可以跟 BackStackState這個parcelable映射起來。基本上BackStackRecord都是可以parcel的類型,出了Op鏈表,Op鏈表映射為BackStackState的int[] mOps;
看完了返回棧,基本上有關Fragment的核心知識點都了解的差不多了,當然我們也跳過了一些知識點,比如有關動畫的部分、ChildFragmentManager等。但基本上Fragment在使用到遇到問題的部分,都可以從上面的分析中找到原因。