官方是從3.0
開始引入Fragment
的,在文檔中有對於其使用的詳細介紹,能夠看出來,在它剛出現時你們對於它是十分推崇的。然而隨着使用Fragment
開發的項目愈來愈多,在一些複雜場景下逐漸暴露出了它的一些問題,對因而否繼續使用Fragment
你們也有不一樣的見解。目前在項目中也有大量以前留下來的使用 Fragment
的代碼, monkey
有時會跑出一些莫名奇妙的問題,在這裏咱們對於使用Fragment
的好壞先不作評價,而是看看它內部整個的實現過程,學習它的思想,在之後出現問題的時候也方便排查,同時你們能夠根據本身的需求來決定是否使用Fragment
。java
Fragment
事務的執行過程在操做Fragment
時,第一件是就是經過Activity
的getFragmentManger()
方法獲得一個FragmentManager
對象:bash
Fragment manager = getFragmentManager();
複製代碼
咱們看一下Activity.java
中的這個方法:ide
final FragmentController mFragments = FragmentController.createController(new HostCallbacks())
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this);
}
}
複製代碼
能夠看到getFragmentManager()
調用的是FragmentController
的接口,那麼咱們再看看這個類對應的方法:oop
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
給包裝了起來,真正完成任務的是FragmentHostCallback
:佈局
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManagerImpl;
}
複製代碼
那麼結論就是咱們實際獲得的是Activity#mFragments#mHost#mFragmentManagerImpl
這個變量,獲得這個實例以後,咱們經過它的beginTransition
方法獲得一個事務:post
FragmentTransation transation = manager.beginTransation();
複製代碼
咱們看一下這個FragmentTransation
到底是個什麼東西,FragmentManagerImpl
是FragmentManager
的一個內部類,它實現了該接口:學習
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
public FragmentTransaction beginTransation() {
return new BackStackRecord(this);
}
}
複製代碼
原來這個FragmentTransation
也是一個接口,它的實現是BackStackRecord
,而且在每次 beginTransaction
時都是返回一個新的事務對象,包括以後進行的後退操做都是經過這個事務對象來管理的,這個對象中保存了建立它的FragmentManagerImpl
實例。 BackStackRecord
實際上是BackStackState
的一個內部類,咱們平時就是經過它來進行add、remove、replace、attach、detach、hide、show
等操做的,進入源碼看一下這些操做背後都幹了些什麼:ui
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 以前已經有了tag,那麼是不容許改變它的。
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
//若是這個fragment已經有了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++;
}
}
複製代碼
看完上面這段代碼,咱們能夠獲得下面這些信息:this
BackStackRecord
當中的一個鏈表。attach、detach、hide、show、remove
這五個操做時是不須要傳入containerViewId
的,由於在執行這些操做以前這個Fragment
必然已經通過了add
操做,它的containerId
是肯定的。add
操做時,須要保證Fragment
的mTag
和mContainerViewId
在不爲空時(也就是這個 Fragment
實例以前已經執行過add
操做),它們和新傳入的tag
和containerViewId
必須是相同的,這是由於在FragmentManager
的mActive
列表中保存了全部被添加進去的Fragment
,而其提供的 findFragmentById/Tag
正是經過這兩個字段做爲判斷的標準,所以不容許同一個實例在列表當中重複出現兩次。replace
操做和add
相似,它增長了一個額外條件,就是containerViewId
不爲空,由於replace
須要知道它是對哪一個container
進行操做,後面咱們會看到replace
實際上是一個先remove
再add
的過程,所以`add 的那些判斷條件一樣適用。那麼這個鏈表中的操做何時被執行呢,看一下commit()
操做:spa
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
//已經有事務處理,拋出異常。
}
mCommited = true;
if (mAddToBackState) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
}
複製代碼
它最後調用了FragmentManager
的enqueueAction
方法,咱們進去看一下里面作了什麼:
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
//若是Activity已經被銷燬,那麼拋出異常。
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
//由於BackStackRecord實現了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) {
//已經有操做在執行,拋出異常
}
if (Looper.myLooper() != mHost.getHandler().getLooper()) {
//不在主線程中執行,拋出異常
}
boolean didSomething = false;
while (true) {
int numActions;
sychronized(this) {
if (mPendingActions == null || mPendingActions.size == 0) {
break;
}
//指望須要執行的事務個數
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++) {
//好吧,這裏就又回調了BackStackRecord的run()方法
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}
}
複製代碼
commit
以後,把BackStackRecord
加入到FragmentManagerImpl
的mPendingActions
中,並且經過查看FragmentManager
的源碼也能夠發現,全部對mPendingActions
的添加操做只有這一個地方調用,Handler post
一個Runnable mExecCommit
出去,當這個Runnable
執行時調用execPendingActions()
方法。execPendingActions
它會拿出這個mPendingActions
當中的全部Runnable
執行(若是它是 BackStackRecord
調用過來的,那麼就是調用BackStackRecord
的run
方法),並把這個列表清空。在每一個BackStackRecord
的run
方法執行時,它是經過遍歷BackStackRecord
鏈表當中每一個節點的cmd
來判斷咱們以前經過FragmentTransation
加入指望執行的那些操做的。execPendingActions
這個方法很關鍵,由於它決定了咱們添加的操做何時會被執行,咱們看下還有那些地方調用到了它,爲何要分析這個呢,由於咱們在項目當中發現不少來自於Fragment
的異常都是由咱們後面談論的moveToState
方法拋出的,而moveToState
執行的緣由是execPendingActions
被調用了,所以瞭解它被調用的時機是咱們追蹤問題的關鍵,關於調用的時機,咱們都總結在下面的註釋當中了:<!-- Activity.java -->
final void performStart() {
mFragments.execPendingActions(); //有可能在 Activity 的 onStart 方法執行前
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() { //這個調用在 onDestroy 以前。
execPendingActions();
}
public boolean popBackStackImmediate() {
executePendingTransactions();
}
public boolean popBackStackImmediate(String name, int flags) {
executePendingTransactions();
}
複製代碼
關於FragmentManager
的討論咱們先暫時放一放,看一下BackStackRecord
是怎麼執行鏈表內部的操做的:
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以前的記下來是爲了後退棧準備的。
if (mAddToBackStack) {
old.mBackStackNesting += 1;
}
mManager.removeFragment(old, transition, transitionStyle);
}
}
if (f != null) {
f.addFragment(f, false);
}
break;
//後面的remove,hide,show,attach,detach就是調用了FragmentManager中相應的方法,沒什麼特別的,就不貼出來了
case xxx:
}
}
op = op.next;
}
//mCurState此時爲FragmentManager當前的狀態,其他的參數不用管。
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
複製代碼
咱們來看一下FragmentManagerImpl
對應的addFragment
等操做:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
makeActive(fragment); //加入到mActive列表中。
if (!fragment.mDetached) {
if (mAdd.contains(fragment)) {
//已經在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;
//這裏會根據是否加入後退棧來判斷新的狀態,最後會影響到Fragment生命週期的調用,若是是沒有加入後退棧的,那麼會多調用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列表中已經有,拋出異常,
}
mAdded.add(fragment);
fragment.mAdded = true;
moveToState(fragment, mCurState, transition, transitionStyle, false);
}
}
}
複製代碼
這裏的操做不少,咱們須要明白如下幾點:
mActive
和mAdded
的區別:mActive
表示執行過add
操做,而且其沒有被移除(移除表示的是在不加入後退棧的狀況下被removeFragment
),全部被動改變Fragment
狀態的調用都是遍歷這個列表;而 mAdded
則表示執行過add
操做,而且沒有執行detachFragment/removeFragment
,也就是說Fragment
是存在容器當中的,可是有多是被隱藏的(hideFragment
)。attachFragment
必須保證其狀態是 mDetach
的,而該屬性的默認值是 false
,只有在執行過 detach
方法後,才能執行,執行它會把 f.mView
從新加入到 container
中。detachFragment
會把 f.mView
從 container
中移除。removeFragment
和 detachFragment
會強制改變 Fragment
的狀態,這是由於它們須要改變 Fragment
在佈局中的位置,而這經過被動地接收 FragmentManager
狀態(即所在Activity
的狀態)是沒法實現的。在 removeFragment
時,會根據是否加入後退棧來區分,若是假如了後退棧,由於有可能以後會回退,而回退時遍歷的是 mActive
列表,若是把它的狀態置爲Fragment.INITIALZING
,那麼在 moveToState
方法中就會走到最後一步,把它從mActive
列表中移除,就找不到了也就沒法恢復,所以這種狀況下Fragment
最終的狀態的和detachFragment
是相同的。detachFragment
時,會把 mDetach
置爲true
,這種狀況下以後能夠執行 attachFragment
操做但不能執行 addFragment
操做;removeFragment
以後能夠執行 addFragment
操做但不能執行 attachFragment
操做。showFragment
和 hideFragment
並不會主動改變 Fragment
的狀態,它僅僅是回調 onHiddenChanged
方法,其狀態仍是跟着 FragmentManager
來走。mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
複製代碼
這個方法纔是 Fragment
的核心,它的思想就是根據 Fragment
指望進入的狀態和以前的狀態進行對比,從而調用 Fragment
相應的生命週期,那麼問題就來,什麼是指望進入的狀態呢,我認爲能夠這麼理解:
Fragment
和 FragmentManager
的狀態不一致,這時須要發生變化。Fragment
和 FragmentManager
的狀態是否一致,由於 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.
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加入進去
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) { //新的狀態高於當前狀態
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) { //新的狀態低於當前狀態
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(); //調用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的全部狀態恢復成初始狀態,至關於它是一個全新的Fragment。
} else {
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
f.mChildFragmentManager = null;
}
}
}
}
}
複製代碼
到 moveToState
方法中,咱們看到了許多熟悉的面孔,就是咱們平時最常談到的 Fragment
的生命週期,經過這段代碼咱們就能夠對它的生命週期有更加直觀的理解。