一刽宪、概述
官方是從3.0
開始引入Fragment
的厘贼,在文檔中有對于其使用的詳細(xì)介紹,可以看出來纠屋,在它剛出現(xiàn)時大家對于它是十分推崇的涂臣。然而隨著使用Fragment
開發(fā)的項(xiàng)目越來越多,在一些復(fù)雜場景下逐漸暴露出了它的一些問題售担,對于是否繼續(xù)使用Fragment
大家也有不同的看法赁遗。目前在項(xiàng)目中也有大量之前留下來的使用 Fragment
的代碼, monkey
有時會跑出一些莫名奇妙的問題族铆,在這里我們對于使用Fragment
的好壞先不做評價岩四,而是看看它內(nèi)部整個的實(shí)現(xiàn)過程,學(xué)習(xí)它的思想哥攘,在以后出現(xiàn)問題的時候也方便排查剖煌,同時大家可以根據(jù)自己的需求來決定是否使用Fragment
材鹦。
二、Fragment
事務(wù)的執(zhí)行過程
在操作Fragment
時耕姊,第一件是就是通過Activity
的getFragmentManger()
方法得到一個FragmentManager
對象:
Fragment manager = getFragmentManager();
我們看一下Activity.java
中的這個方法:
final FragmentController mFragments = FragmentController.createController(new HostCallbacks())
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this);
}
}
可以看到getFragmentManager()
調(diào)用的是FragmentController
的接口桶唐,那么我們再看看這個類對應(yīng)的方法:
private final FragmentHostCallback<?> mHost;
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks)
}
private FragmentController(FragmentHostCallback<?> callbacks) {
mHost = callbacks;
}
public FragmentManager getFragmentManager() {
return mHost.getFragmentManagerImpl();
}
原來FragmentController
是把一個callback
給包裝了起來,真正完成任務(wù)的是FragmentHostCallback
:
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManagerImpl;
}
那么結(jié)論就是我們實(shí)際得到的是Activity#mFragments#mHost#mFragmentManagerImpl
這個變量茉兰,得到這個實(shí)例之后尤泽,我們通過它的beginTransition
方法得到一個事務(wù):
FragmentTransation transation = manager.beginTransation();
我們看一下這個FragmentTransation
究竟是個什么東西,FragmentManagerImpl
是FragmentManager
的一個內(nèi)部類规脸,它實(shí)現(xiàn)了該接口:
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
public FragmentTransaction beginTransation() {
return new BackStackRecord(this);
}
}
原來這個FragmentTransation
也是一個接口坯约,它的實(shí)現(xiàn)是BackStackRecord
,并且在每次 beginTransaction
時都是返回一個新的事務(wù)對象莫鸭,包括之后進(jìn)行的后退操作都是通過這個事務(wù)對象來管理的闹丐,這個對象中保存了創(chuàng)建它的FragmentManagerImpl
實(shí)例。
BackStackRecord
其實(shí)是BackStackState
的一個內(nèi)部類被因,我們平時就是通過它來進(jìn)行add卿拴、remove、replace梨与、attach巍棱、detach、hide蛋欣、show
等操作的航徙,進(jìn)入源碼看一下這些操作背后都干了些什么:
final class BackStackRecord extends FragmentTransation implements FragmentManager.BackStackEntry, Runnable {
public FragmentTransation add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
public FragmentTransation add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
public FragmentTransation add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
}
public FragmentTransation replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
public FragmentTransation replace(int containerViewId, Fragment fragment, String tag) {
if (containerViewId == 0) {
//replace操作必須要指定containerViewId.
}
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
}
public FragmentTransaction remove(Fragment fragment) {
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);
}
public FragmentTransaction hide(Fragment fragment) {
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);
}
public FragmentTransaction show(Fragment fragment) {
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);
}
public FragmentTransaction attach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_ATTACH;
op.fragment = fragment;
addOp(op);
}
public FragmentTransaction detach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_DETACH;
op.fragment = fragment;
addOp(op);
}
//新建一個操作,并給操作賦值陷虎。
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)) {
//如果這個 fragment 之前已經(jīng)有了tag到踏,那么是不允許改變它的。
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
//如果這個fragment已經(jīng)有了mFragmentId尚猿,那么不允許改變它窝稿。
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
//把操作添加到鏈表之中。
void addOp() {
if (mHead == null) {
mHead = mTail = op;
} else {
op.prev = mTail;
mTail.next = op;
mTail = op;
}
mNumOp++凿掂;
}
}
看完上面這段代碼伴榔,我們可以得到下面這些信息:
- 我們調(diào)用這些操作之后僅僅是把這個操作添加到了
BackStackRecord
當(dāng)中的一個鏈表。 - 在進(jìn)行
attach庄萎、detach踪少、hide、show、remove
這五個操作時是不需要傳入containerViewId
的,因?yàn)樵趫?zhí)行這些操作之前這個Fragment
必然已經(jīng)經(jīng)過了add
操作挚躯,它的containerId
是確定的。 - 在執(zhí)行
add
操作時集漾,需要保證Fragment
的mTag
和mContainerViewId
在不為空時(也就是這個Fragment
實(shí)例之前已經(jīng)執(zhí)行過add
操作)切黔,它們和新傳入的tag
和containerViewId
必須是相同的,這是因?yàn)樵?code>FragmentManager的mActive
列表中保存了所有被添加進(jìn)去的Fragment
具篇,而其提供的findFragmentById/Tag
正是通過這兩個字段作為判斷的標(biāo)準(zhǔn)纬霞,因此不允許同一個實(shí)例在列表當(dāng)中重復(fù)出現(xiàn)兩次。 -
replace
操作和add
類似驱显,它增加了一個額外條件险领,就是containerViewId
不為空,因?yàn)?code>replace需要知道它是對哪個container
進(jìn)行操作秒紧,后面我們會看到replace
其實(shí)是一個先remove
再add
的過程,因此`add 的那些判斷條件同樣適用挨下。
那么這個鏈表中的操作什么時候被執(zhí)行呢熔恢,看一下commit()
操作:
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
//已經(jīng)有事務(wù)處理,拋出異常臭笆。
}
mCommited = true;
if (mAddToBackState) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
}
它最后調(diào)用了FragmentManager
的enqueueAction
方法叙淌,我們進(jìn)去看一下里面做了什么:
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
//如果Activity已經(jīng)被銷毀,那么拋出異常愁铺。
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
//因?yàn)锽ackStackRecord實(shí)現(xiàn)了Runnable接口鹰霍,把加入到其中。
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
Runnable mExecCommit = new Runnable {
public void run() {
execPendingActions();
}
}
public boolean execPendingActions() {
if (mExecutingActions) {
//已經(jīng)有操作在執(zhí)行茵乱,拋出異常
}
if (Looper.myLooper() != mHost.getHandler().getLooper()) {
//不在主線程中執(zhí)行茂洒,拋出異常
}
boolean didSomething = false;
while (true) {
int numActions;
sychronized(this) {
if (mPendingActions == null || mPendingActions.size == 0) {
break;
}
//期望需要執(zhí)行的事務(wù)個數(shù)
numActions = mPendingActions.size();
if (mTmpActions == null || mTmpActions.length < numActions) {
mTmpActions = new Runnable[numActions];
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
}
mExecutingActions = true;
for (int i = 0; i < numActions; i++) {
//好吧,這里就又回調(diào)了BackStackRecord的run()方法
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}
}
- 在調(diào)用
commit
之后瓶竭,把BackStackRecord
加入到FragmentManagerImpl
的mPendingActions
中督勺,而且通過查看FragmentManager
的源碼也可以發(fā)現(xiàn),所有對mPendingActions
的添加操作只有這一個地方調(diào)用斤贰, - 當(dāng)其大小為1時智哀,會通過主線程的
Handler post
一個Runnable mExecCommit
出去,當(dāng)這個Runnable
執(zhí)行時調(diào)用execPendingActions()
方法荧恍。 -
execPendingActions
它會拿出這個mPendingActions
當(dāng)中的所有Runnable
執(zhí)行(如果它是BackStackRecord
調(diào)用過來的瓷叫,那么就是調(diào)用BackStackRecord
的run
方法),并把這個列表清空送巡。在每個BackStackRecord
的run
方法執(zhí)行時摹菠,它是通過遍歷BackStackRecord
鏈表當(dāng)中每個節(jié)點(diǎn)的cmd
來判斷我們之前通過FragmentTransation
加入期望執(zhí)行的那些操作的。 - 可以看出
execPendingActions
這個方法很關(guān)鍵骗爆,因?yàn)樗鼪Q定了我們添加的操作什么時候會被執(zhí)行辨嗽,我們看下還有那些地方調(diào)用到了它,為什么要分析這個呢淮腾,因?yàn)槲覀冊陧?xiàng)目當(dāng)中發(fā)現(xiàn)很多來自于Fragment
的異常都是由我們后面談?wù)摰?code>moveToState方法拋出的糟需,而moveToState
執(zhí)行的原因是execPendingActions
被調(diào)用了屉佳,因此了解它被調(diào)用的時機(jī)是我們追蹤問題的關(guān)鍵,關(guān)于調(diào)用的時機(jī)洲押,我們都總結(jié)在下面的注釋當(dāng)中了:
<!-- Activity.java -->
final void performStart() {
mFragments.execPendingActions(); //有可能在 Activity 的 onStart 方法執(zhí)行前
mInstrumentation.callActivityOnStart(this);
}
final void performResume() {
performRestart();
mFragments.execPendingActions(); //如果是從 Stopped 過來的武花,那么有可能在 onStart 到 onResume 之間。
....
mInstrumentation.callActivityOnResume(this);
....
mFragments.dispatchResume();
mFragments.execPendingActions(); //有可能在 onResume 到 onPause 之間杈帐。
}
<!-- FragmentManager.java -->
public void dispatchDestroy() { //這個調(diào)用在 onDestroy 之前体箕。
execPendingActions();
}
public boolean popBackStackImmediate() {
executePendingTransactions();
}
public boolean popBackStackImmediate(String name, int flags) {
executePendingTransactions();
}
關(guān)于FragmentManager
的討論我們先暫時放一放,看一下BackStackRecord
是怎么執(zhí)行鏈表內(nèi)部的操作的:
public void run() {
Op op = mHead;
while (op != null) {
switch(op.cmd) {
case OP_ADD:
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
break;
case OP_REPLACE: {
Fragment f = op.fragment;
int containerId = f.mContainerId;
if (mManager.mAdded != null) {
//遍歷 mAdded列表挑童,找到 containerId 相同的 old Fragment.
if (old == f) {
op.fragment = f = null;
} else {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old); //這里要把replace之前的記下來是為了后退棧準(zhǔn)備的累铅。
if (mAddToBackStack) {
old.mBackStackNesting += 1;
}
mManager.removeFragment(old, transition, transitionStyle);
}
}
if (f != null) {
f.addFragment(f, false);
}
break;
//后面的remove,hide,show,attach,detach就是調(diào)用了FragmentManager中相應(yīng)的方法,沒什么特別的站叼,就不貼出來了
case xxx:
}
}
op = op.next;
}
//mCurState此時為FragmentManager當(dāng)前的狀態(tài)娃兽,其余的參數(shù)不用管。
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
我們來看一下FragmentManagerImpl
對應(yīng)的addFragment
等操作:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
makeActive(fragment); //加入到mActive列表中尽楔。
if (!fragment.mDetached) {
if (mAdd.contains(fragment)) {
//已經(jīng)在mAdded列表投储,拋出異常。
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (moveToStateNow) {
moveToState(fragment);
}
}
}
public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
final boolean inactive = !fragment.isInBackStack();
if (!fragment.mDetach || inactive) {
if (mAdded != null) {
mAdded.remove(fragment); //從mAdded列表中移除阔馋。
}
fragment.mAdded = false;
fragment.mRemoving = true;
//這里會根據(jù)是否加入后退棧來判斷新的狀態(tài)玛荞,最后會影響到Fragment生命周期的調(diào)用,如果是沒有加入后退棧的呕寝,那么會多調(diào)用onDestroy勋眯、onDetach方法。
moveToState(fragment, inactive ? Fragment.INITIALZING : Fragment.CREATED, transition, transitionStyle, false);
}
}
public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
if (!fragment.mHidden) {
fragment.mHidden = true;
if (fragment.mView != null) {
fragment.mView.setVisibility(View.GONE);
}
}
fragment.onHiddenChanged(true);
}
public void showFragment(Fragment fragment, int transition, int transitionStyle) {
if (fragment.mHidden) {
fragment.mHidden = false;
if (fragment.mView != null) {
fragment.mView.setVisibility(View.VISIBLE);
}
fragment.onHiddenChanged(false);
}
}
public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
if (!fragment.mDetached) {
fragment.mDetached = true;
if (fragment.mAdded) {
if (mAdded != null) {
mAdded.remove(fragment);
}
fragment.mAdded = false;
moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
}
}
}
public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
if (fragment.mDetached) {
if (!fragment.mAdded) {
if (mAdded.contains(fragment)) {
//mAdded列表中已經(jīng)有下梢,拋出異常凡恍,
}
mAdded.add(fragment);
fragment.mAdded = true;
moveToState(fragment, mCurState, transition, transitionStyle, false);
}
}
}
這里的操作很多,我們需要明白以下幾點(diǎn):
-
mActive
和mAdded
的區(qū)別:mActive
表示執(zhí)行過add
操作怔球,并且其沒有被移除(移除表示的是在不加入后退棧的情況下被removeFragment
)嚼酝,所有被動改變Fragment
狀態(tài)的調(diào)用都是遍歷這個列表;而mAdded
則表示執(zhí)行過add
操作竟坛,并且沒有執(zhí)行detachFragment/removeFragment
闽巩,也就是說Fragment
是存在容器當(dāng)中的,但是有可能是被隱藏的(hideFragment
)担汤。 -
attachFragment
必須保證其狀態(tài)是mDetach
的涎跨,而該屬性的默認(rèn)值是false
,只有在執(zhí)行過detach
方法后崭歧,才能執(zhí)行隅很,執(zhí)行它會把f.mView
重新加入到container
中。 -
detachFragment
會把f.mView
從container
中移除率碾。 -
removeFragment
和detachFragment
會強(qiáng)制改變Fragment
的狀態(tài)叔营,這是因?yàn)樗鼈冃枰淖?Fragment
在布局中的位置屋彪,而這通過被動地接收FragmentManager
狀態(tài)(即所在Activity
的狀態(tài))是無法實(shí)現(xiàn)的。在removeFragment
時绒尊,會根據(jù)是否加入后退棧來區(qū)分畜挥,如果假如了后退棧,因?yàn)橛锌赡苤髸赝擞て祝赝藭r遍歷的是mActive
列表蟹但,如果把它的狀態(tài)置為Fragment.INITIALZING
,那么在moveToState
方法中就會走到最后一步谭羔,把它從mActive
列表中移除华糖,就找不到了也就無法恢復(fù),因此這種情況下Fragment
最終的狀態(tài)的和detachFragment
是相同的瘟裸。 - 在加入后退棧時客叉,
detachFragment
時,會把mDetach
置為true
景描,這種情況下之后可以執(zhí)行attachFragment
操作但不能執(zhí)行addFragment
操作;removeFragment
之后可以執(zhí)行addFragment
操作但不能執(zhí)行attachFragment
操作秀撇。 -
showFragment
和hideFragment
并不會主動改變Fragment
的狀態(tài)超棺,它僅僅是回調(diào)onHiddenChanged
方法,其狀態(tài)還是跟著FragmentManager
來走呵燕。
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
這個方法才是 Fragment
的核心棠绘,它的思想就是根據(jù) Fragment
期望進(jìn)入的狀態(tài)和之前的狀態(tài)進(jìn)行對比,從而調(diào)用 Fragment
相應(yīng)的生命周期再扭,那么問題就來氧苍,什么是期望進(jìn)入的狀態(tài)呢,我認(rèn)為可以這么理解:
- 用戶沒有進(jìn)行主動操作泛范,但是
Fragment
和FragmentManager
的狀態(tài)不一致让虐,這時需要發(fā)生變化。 - 用戶進(jìn)行了主動操作罢荡,無論
Fragment
和FragmentManager
的狀態(tài)是否一致赡突,因?yàn)?Fragment
的狀態(tài)發(fā)生了變化,因此這時也需要執(zhí)行区赵。
這里我們?yōu)榱撕啽闫鹨娤瓤匆幌潞蜕芷谟嘘P(guān)的代碼:
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.
void moveToState(int newState, int transit, int transitStyle, boolean always) {
if (!always && mCurState == newState) {
return;
}
mCurState = newState;
if (mActive != null) {
for (int i = 0; i < mActive.size; i++) {
//在addFragment中通過makeActive加入進(jìn)去
Fragment f = mActive.get(i);
moveToState(f, newState, transit, transitStyle, false);
}
}
}
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
if (f.mState < newState) { //新的狀態(tài)高于當(dāng)前狀態(tài)
switch(f.mState) {
case Fragment.INITIALZING:
f.onAttach(mHost.getContext());
if (f.mParentFragment == null) {
mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
}
if (f.mFromLayout) {
f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mCotainerId != null) {
cotainer = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
//no view found
}
}
f.mContainer = container;
f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mView != null) {
f.mInnerView = f.mView;
if (Build.VERSION.SDK >= 11) {
ViewCompact.setSaveFromParentEnable(f.mView, false);
} else {
f.mView = NoSaveStateFrameLayout.wap(f.mView);
}
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
}
case Fragment.ACTIVITY_CRREATED:
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
f.performResume();
}
}
} else if (f.mState > newState) { //新的狀態(tài)低于當(dāng)前狀態(tài)
switch(f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
f.performPause();
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
f.performStop();
}
case Fragment.STOPPED::
if (newState < Fragment.STOPPED) {
f.performReallyStop();
}
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
f.performDestroyView(); //調(diào)用onDestory()
if (f.mView != null && f.mContainer != null) {
f.mContainer.removeView(f.mView); //把Fragment的View從視圖中移除惭缰。
}
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (f.mAnimationAway != null) {
...
} else {
if (!f.mRetaining) {
f.performDestory();
} else {
f.mState = Fragment.INITIALIZING;
}
f.onDetach();
if (!f.mRetaining) {
makeInActive(f);//把Fragment從mActive中移除,并把Fragment的所有狀態(tài)恢復(fù)成初始狀態(tài)笼才,相當(dāng)于它是一個全新的Fragment漱受。
} else {
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
f.mChildFragmentManager = null;
}
}
}
}
}
到 moveToState
方法中,我們看到了許多熟悉的面孔骡送,就是我們平時最常談到的 Fragment
的生命周期昂羡,通過這段代碼我們就可以對它的生命周期有更加直觀的理解絮记。