Fragment 是android开发中最常用的组件之一,用了好几年,我都不知道Fragment到底是个什么东西,Activity加载Fragment的原理是怎样的,为什么官方会叫它为碎片?直到前段时间因为工作需要,从头看来一遍Fragment的源代码,然后就有了本文。

本文将从commit开始一步步带你走向Fragmnt的生命周期!!

经典的Frgment加载

从最经典的Activity加载Fragment的流程说起,如下所示,是Activity加载Frgment的例子代码。

1
2
3
4
5
6
7
8
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<FrameLayout android:id="@+id/content" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground" />

</LinearLayout>

1
2
3
4
5
6
7
8
9
10
11
12
public static class DetailsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().replace(R.id.content, details).commit();
}
}
}

Activity是通过getFragmentManager().beginTransaction()来进行加载Fragment的,我们就从这句话开始吧!

fragment状态的切换时通过beginTransaction()方法来实现的,而beginTransaction()是FragmentManager的抽象方法,而FragmentManager真正的实现是FragamentManager的内部类FragmentManagerImpl,因此在FragmentManagerImpl中查找beginTransaction方法,最终方法发现beginTransaction真正生成的实例是BackStackRecord

1
2
3
4
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}

由此可以知道replace,add,hide,show等等对Fragment状态的操作的真正实现都是在BackStackRecord的对应方法中实现,再看BackStackRecord的replace、add方法最终都会跳转到doAddOP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
}

上面的代码中最核心的部分是最后的四行,可以看到内次replace都会new一个Op,那么OP到底是个什么东西?

1
2
3
4
5
6
7
8
9
10
11
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}

这是OP的源代码,可以看到OP有next和prev的字段,这两个字段的属性都是Op本身,并且它本身还有一些自带的属性,到现在依然不知道这个Op到底是什么,继续看addOp方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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++;
}

从if-else语句中,再结合Op的结构,可以看出Op是一个双向链表,而addOp方法的作用是将replce生成的Op添加到当前链表中,到现在我们已经知道Op是个双向链表了,如下图所示:
双向链表图

那么Op到底有什么用,从目前看到的源码来看我们还是不知道。继续看BackStackRecord的show、remove等方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public FragmentTransaction remove(Fragment fragment) {
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);

return this;
}

public FragmentTransaction hide(Fragment fragment) {
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);

return this;
}

public FragmentTransaction show(Fragment fragment) {
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);

return this;
}

从上面的代码看出,无论是replcae、add还是remove、hide、show操作,都会new一个Op并加到链表中,而每次添加链表时,Op的cmd和fragment都不一样,因此可以猜测Op链表实质上是操作fragment的命令链表,而执行命令的操作由commit等方法来完成的

继续看BackStackRecord的commit方法,在前面我们猜测fragment状态的控制是由这个方法完成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public int commit() {
return commitInternal(false);
}

public int commitAllowingStateLoss() {
return commitInternal(true);
}

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;
}

上面是commit的源代码,核心代码是倒数第二行,看到命令的执行最终还是有mManager控制,但是真的是这样吗?mManager到底是在哪个地方初始化的,还记得FragmentManagerImpl的beginTransaction方法吗?

跳转到FragmentManagerImpl中,继续看enqueueAction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
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().post(mExecCommit);
}
}
}

从这个方法可以看出,真正执行执行Op命令的是mExecCommit这个线程,继续看mExecCommit线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
/**
* Only call from main thread!
*/
public boolean execPendingActions() {
if (mExecutingActions) {
throw new IllegalStateException("Recursive entry to executePendingTransactions");
}

if (Looper.myLooper() != mHost.getHandler().getLooper()) {
throw new IllegalStateException("Must be called from main thread of process");
}

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];
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
}

mExecutingActions = true;
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}

if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null && f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
if (!loadersRunning) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
return didSomething;
}

这是mExecCommit线程真正执行的方法,但是还是看不到任何关于Fragment被操作的痕迹,也看不到任何的Op命令,继续看execPendingActions方法,在这个方法中,有个代码片段吸引了我的注意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
``
在这我看到了run方法!!!而mTmpActions是mPendingActions的数组化,在`enqueueAction`中,mPendingActions是加载的是BackStackRecord!!!!!**而BackStackRecord是实现Runnable接口的!!**,现在一切都清楚了,真正执行Op命令的还是在BackStackRecord中。</br>
继续看BackStackRecord的run方法。
```java
while (op != null) {
...
switch (op.cmd) {
...
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.hideFragment(f, transition, transitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.showFragment(f, transition, transitionStyle);
} break;
...
}
op = op.next;
}

上面是run的部分代码,到这里我们终于看到了希望见到的Op命令!!在run方法中,while会遍历并执行Op链表中的所有命令,执行命令的过程最终还是通过mManager来执行,拿addFragment来示例,

1
2
3
4
5
6
public void addFragment(Fragment fragment, boolean moveToStateNow) {
...
if (moveToStateNow) {
moveToState(fragment);
}
}

从上面的代码看到真正控制Fragment状态的是moveToState方法,多次跳转后最终跳转到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
...
if (f.mState < newState) {
...
switch (f.mState) {
case Fragment.INITIALIZING:
...
f.onAttach(mHost.getContext());
...
case Fragment.CREATED:
...
// onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) return 的View将在这里被指向
f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
//onActivityCreated(savedInstanceState); 在这里被调用
f.performActivityCreated(f.mSavedFragmentState);
// Fragment的View将在这被加载进Activity中
container.addView(f.mView);
...
case Fragment.ACTIVITY_CREATED:
case Fragment.STOPPED:
...
// onStart() 在这里被调用
f.performStart();
...
case Fragment.STARTED:
...
// onResume() 在这里被调用
f.performResume();
...
}
}else if(f.mState > newState){
switch (f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
// onPause()在这被调用
f.performPause();
f.mResumed = false;
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
// onStop()在这被调用
f.performStop();
}
...
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (f.mAnimatingAway != null) {
f.mStateAfterAnimating = newState;
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
// onDestroy 在这被回调
f.performDestroy();
}
}
}
}
}

由于篇幅所限,上面的只是代码片段,从moveToState方法中,我们见到了Fragment的整个生命周期!!!

在第一个switch的case Fragment.CREATED:中Fragment 的 view 的被加载,不知道你是否还记得文章最开始的那个xml中id为content的FrameLayout,在这个case下,fragment的view正是被加载到这个Layout中!!!!!!

这里有个有意思的地方,两个switch都是没有break语句的,当我们第一次add是,f.mState的默认状态为INITIALIZING,该状态下,fragemnt就要走完onAttach–onResume的所有流程,通过上面的代码,最终可以知道,fragment的全部生命周期都由mState这个字段决定。

到了现在getFragmentManager().beginTransaction().replace(R.id.content, details).commit();这句话我们算是完全清楚了它的工作流程。

总结

在加载Fragament中,beginTransaction()创建了一个BackStackRecord对象,该对象实现了Runnable接口,replace方法将replace命令加载到BackStackRecord的Op链表中,当开发者调用commit方法时,commit方法将以事件的形式生成Op命令并将Op传递给FragmentManagerImpl,FragmentManagerImpl在Activity的handler中启动mExecCommit线程,mExecCommit线程执行BackStackRecord线程,在BackStackRecord的run方法里面,遍历所有Op链表,依次执行Op链表中所有的命令,run的switch根据Op命令,动态执行调用FragmentManagerImpl的replace、add、show等方法,在FragmentManagerImpl中的replace、add、show等方法最终都会调用moveToState方法,而整个Fragment的生命周期都在这个moveToState方法中。在这方法中,在create中,fragment最终将被加载找eginTransaction().replace(R.id.content, details),replace第一个参数所指向的Layout中!!加载View完成后,moveToState将根据fragment的mState继续执行Fragment的生命周期。

到现在整个Fragment的生成加载,生命周期我们全部了解完成了,得出的最后一个结论是Fragment本质上是嵌入在Activity中一个ViewGroup的View,但是谷歌给这个View赋予了生命周期,看到Fragment后终于明白Square为什么要建议放弃Fragment了!!!

虽然感觉Square公司有点偏激,但是作为一般开发者,在能用自定义View的情况下还是尽量不要用Fragment,因为Fragment实在太复杂了,一旦出现奇怪的问题,根本找不到哪个地方出的错,说多了都是泪!!!