前言:
最近看了 fragment 源码,为方便自己以后查看,在此记录一下
源码版本:Androidx
正文:
在此,我们看一下 fragment 中 add、replace、commit等方法
先放一段伪代码:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
HomeFragment homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.main_tab_fl, homeFragment);
fragmentTransaction.replace(R.id.main_tab_fl,homeFragment);
fragmentTransaction.commit();
1、先看第一行 FragmentManager fragmentManager = getSupportFragmentManager();
看源码一直往下走
所在类:FragmentActivity.java
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
所在类:FragmentController.java
public FragmentManager getSupportFragmentManager() {
return mHost.getFragmentManagerImpl();
}
所在类:FragmentHostCallback.java
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}
到此,我们可以看到 返回的类型为 FragmentManagerImpl ,也走到了最后,接着我们在 FragmentManagerImpl.java 这个类中找到 beginTransaction 这个方法。
2、这样就到了第二行:FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
所在类:FragmentManagerImpl.java
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
所在类:BackStackRecord.java
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
由上面一下代码可以看出 fragment 的实现路线是
FragmentActivity
-> FragmentController
-> FragmentHostCallback
-> FragmentManagerImpl
-> BackStackRecord
至此,我们可以看到有关于 FragmentManage r中的操作都是在 FragmentManagerImpl 类中提供了实现方法,但是关于其管理等操作,最后还是封装进了 BackStackRecord 中。接下来我们就在 BackStackRecord 中看看 add、replace、commit 方法
第三行只是 new 一个 fragment,方便下面代码观看,此处不看
3、接着进入第四行 fragmentTransaction.add(R.id.main_tab_fl, homeFragment);
我们在 BackStackRecord 中查找 add 方法:
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
// 注意此时的 OP_ADD 字段参数,在后面真正执行 Fragment 的操作时会用到,
// 用来判断当前Fragment 的真正操作是什么?添加 add、移除 remove、隐藏 hide 或者显示 show 等
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
接着继续进入 doAddOp 方法,只看关键部分
private void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
......
if (tag != null) {
// 此处判断 tag 是否为空
}
if (containerViewId != 0) {
// 此处判断传入的 fragment 容器 id 是否为空
}
// 最后进入 addop 方法
addOp(new Op(opcmd, fragment));
}
接着进入 addop 方法中
void addOp(Op op) {
mOps.add(op);
......
}
我们可以看到,源码中将整个操作封装成了一个Op,Op 应该是一个数据结构容器,通过 addOp(op) 方法将其添加到 mOps 列表里面去。这个列表是源码在上面定义的一个链表。
到此 add 方法执行结束,这一步可以理解为是帮我们做一系列的判断和赋值等操作。所有的 添加、替换 等操作都是在 commit 中实现的
4、add 后执行 commit 操作,也就是第 6 行 fragmentTransaction.commit();
在 BackStackRecord 中查找 commit :
@Override
public int commit() {
return commitInternal(false);
}
继续往下走,进入 commitInternal 方法,只放一些关键性代码
int commitInternal(boolean allowStateLoss) {
//只能进行一次commit()操作,否则报异常
if (mCommitted) throw new IllegalStateException("commit already called");
...
mCommitted = true;
//是否加入了回退栈(在addToBackStack()中赋值为true)
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
//this是一个FragmentManagerImpl.OpGenerator对象,BackStackRecord实现了这个接口
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
接着进入 enqueueAction 方法
/**
* 添加一个操作到待操作队列
*
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
//检查状态是否丢失,通常会遇见的两个异常就来自此处的检查
checkStateLoss();
}
......
mPendingActions.add(action);
// 进入 scheduleCommit 方法
scheduleCommit();
}
接着进入 scheduleCommit 方法
void scheduleCommit() {
synchronized (this) {
//存在延期执行的操作
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
//准备执行的操作
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
// 此处执行了异步操作,提交了 mExecCommit
// 放弃所有操作,从新开始新的操作,并且线程安全
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
接下来,进入 mExecCommit 方法
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
// 最终在主线程中执行 execPendingActions(); 方法
execPendingActions();
}
};
再往下走,进入 execPendingActions 方法
/**
* 只能从主线程中调用
*/
public boolean execPendingActions() {
// 步骤一,检查是否是主线程、是否已经在执行、检查状态是否丢失
ensureExecReady(true);
boolean didSomething = false;
// 步骤二,generateOpsForPendingActions会判断当前是否还有任务
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
// 步骤三,去掉多余任务,并执行任务
// 执行任务的时候,会去调用 BackStackRecord 中的 expandOps 方法,里面会执行之前的Ops
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
} finally {
// 重置mExecutingActions = false
// 清空mTmpIsPop和mTmpRecords
cleanupExec();
}
didSomething = true;
}
// 步骤四
doPendingDeferredStart();
burpActive();
return didSomething;
}
步骤三 removeRedundantOperationsAndExecute 和 步骤四 最后都会执行到 moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)
4.1、先看步骤三
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop) {
......
for (int recordNum = 0; recordNum < numRecords; recordNum++) {
......
// 执行 所有出栈入栈的优化操作
int reorderingEnd = recordNum + 1;
if (isRecordPop.get(recordNum)) {
while (reorderingEnd < numRecords
&& isRecordPop.get(reorderingEnd)
&& !records.get(reorderingEnd).mReorderingAllowed) {
reorderingEnd++;
}
}
//执行BackStackRecord的可优化和不可优化Op
executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
}
......
}
继续进入 executeOpsTogether 方法
// executeOpsTogether 函数很复杂但是主要也就这两个函数调用关键。第一个只是取出我们刚刚新建的fragment,
// 最后一个才是真的吧fragment加入到页面上。
// 而我们在代码中调用执行 replace 后,commit提交后就会进入 expandOps 方法中,switch选择进入 OP_REPLACE
private void executeOpsTogether(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
executeOps(records, isRecordPop, startIndex, endIndex);
......
}
继续看 executeOps 方法
private static void executeOps(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
......
if (isPop) {
......
record.executePopOps(moveToState);
} else {
......
record.executeOps();
}
}
}
executeOps 方法中,执行后,会进入 executePopOps(moveToState) 或者 executeOps() 方法中,
而这两个方法,最后都会调用 moveToState(mManager.mCurState, true)。
接下来看看 executePopOps(moveToState) 和 executeOps():
void executePopOps(boolean moveToState) {
......
mManager.moveToState(mManager.mCurState, true);
......
}
void executeOps() {
......
// Added fragments are added at the end to comply with prior behavior.
mManager.moveToState(mManager.mCurState, true);
......
}
继续看看 moveToState(mManager.mCurState, true)
void moveToState(int newState, boolean always) {
......
startPendingDeferredFragments();
......
}
进入 startPendingDeferredFragments,请记住这里,步骤四,也会走到这里
void startPendingDeferredFragments() {
if (mActive == null) return;
......
performPendingDeferredStart(f);
......
}
然后进入 performPendingDeferredStart
public void performPendingDeferredStart(Fragment f) {
......
moveToState(f, mCurState, 0, 0, false);
.....
}
最终进入了 moveToState(f, mCurState, 0, 0, false) 这个方法中,在这里进行 fragment 的创建等,
我们也可以在这看到 为什么 fragment 的生命周期会依次执行 onAttach() -》onCreate() -》onCreateView() .....
现在我们看一下 moveToState(f, mCurState, 0, 0, false) ,太多了,只截取部分
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
......
if (f.mState <= newState) {
switch (f.mState) {
case Fragment.INITIALIZING:
.....
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
f.mCalled = false;
// 此处执行 fragment 的 onAttach,具体的,大家可以点进去看
f.onAttach(mHost.getContext());
......
if (!f.mIsCreated) {
dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
// 此处执行 fragment 的 onCreate
f.performCreate(f.mSavedFragmentState);
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
}
......
case Fragment.CREATED:
ensureInflatedFragmentView(f);
......
// 这里的 mContainerId 就是我们传入的 Fragment 容器 id,
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
// 将根据传入 id 得到的 container 赋值给 mContainer
f.mContainer = container;
......
// 这里会执行 fragment 的 onCreateView
// 点进入会看到 f.performCreateView -> mView = onCreateView(inflater, container, savedInstanceState)
// onCreateView 中返回为 null,所以我们在自己的 fragment 中重写该方法,写入自己的 view
f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
......
// 判断由 onCreateView 得到的 mView 是否为空
if (f.mView != null) {
......
if (container != null) {
// mView 不为空,就添加到 fragment 容器中
******************** 到此,才真正的添加到了容器中 ********************
container.addView(f.mView);
}
......
}
// 剩下的 大家可以自己看看
case Fragment.ACTIVITY_CREATED:
case Fragment.STARTED:
}
} else if (f.mState > newState) {
switch (f.mState) {
case Fragment.RESUMED:
case Fragment.STARTED:
case Fragment.ACTIVITY_CREATED:
case Fragment.CREATED:
}
}
......
}
4.2 步骤四 doPendingDeferredStart();
void doPendingDeferredStart() {
if (mHavePendingDeferredStart) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
进入 startPendingDeferredFragments 中
void startPendingDeferredFragments() {
if (mActive == null) return;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.valueAt(i);
if (f != null) {
performPendingDeferredStart(f);
}
}
}
进入 performPendingDeferredStart 中
public void performPendingDeferredStart(Fragment f) {
if (f.mDeferStart) {
if (mExecutingActions) {
// Wait until we're done executing our pending transactions
mHavePendingDeferredStart = true;
return;
}
f.mDeferStart = false;
// 最后调用 moveToState
moveToState(f, mCurState, 0, 0, false);
}
}
可以看到,最后依然会进入 moveToState(f, mCurState, 0, 0, false) 中,大家可以看上面的 moveToState 的解释
5、执行 replace 操作,也就是第五行中的 fragmentTransaction.replace(R.id.main_tab_fl,homeFragment)
其实 replace 与 add 操作类似,都是帮我们做一系列的判断和赋值等操作
@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
/
@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment,
@Nullable String tag) {
....
// 调用 doAddOp,注意这里的 opcmd = OP_REPLACE
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
进入 doAddOp 方法
private void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
......
// 和 add 中的一样,最后都会进入 addOp,不同的是 opcmd
addOp(new Op(opcmd, fragment));
}
void addOp(Op op) {
mOps.add(op);
......
}
commit 操作提交后,会执行 executeOpsTogether 中的 record.expandOps(mTmpAddedFragments, oldPrimaryNav) 方法,至于commit操作如何走到 executeOpsTogether 方法中的,大家可以看看 4 中 commit 操作的流程
接下来我们看一下 expandOps 方法:
Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
switch (op.cmd) {
case OP_ADD:
case OP_ATTACH:
added.add(op.fragment);
break;
case OP_REMOVE:
case OP_DETACH: {
added.remove(op.fragment);
if (op.fragment == oldPrimaryNav) {
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
opNum++;
oldPrimaryNav = null;
}
}
break;
// 因为 replace 中 op.cmd 为 OP_REPLACE 最后会走进这里在
case OP_REPLACE: {
final Fragment f = op.fragment;
final int containerId = f.mContainerId;
boolean alreadyAdded = false;
// 从后往前遍历 ArrayList<Fragment> 中的Fragment,看一下mContainerId 是否与添加的 Fragment一致
for (int i = added.size() - 1; i >= 0; i--) {
final Fragment old = added.get(i);
// 判断 mContainerId 是否一致
if (old.mContainerId == containerId) {
if (old == f) {
// 如果一致,将 alreadyAdded 置为 true
alreadyAdded = true;
} else {
// This is duplicated from above since we only make
// a single pass for expanding ops. Unset any outgoing primary nav.
if (old == oldPrimaryNav) {
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
opNum++;
oldPrimaryNav = null;
}
final Op removeOp = new Op(OP_REMOVE, old);
removeOp.enterAnim = op.enterAnim;
removeOp.popEnterAnim = op.popEnterAnim;
removeOp.exitAnim = op.exitAnim;
removeOp.popExitAnim = op.popExitAnim;
mOps.add(opNum, removeOp);
added.remove(old);
opNum++;
}
}
}
if (alreadyAdded) {
// alreadyAdded 为 true时,移除mOps中的该实例
mOps.remove(opNum);
opNum--;
} else {
op.cmd = OP_ADD;
added.add(f);
}
}
break;
......
}
}
return oldPrimaryNav;
}
由上面流程可以看出:
其实现为:
- 从后往前遍历 ArrayList<Fragment> 中的Fragment,看一下mContainerId 是否与添加的 Fragment一致
- 如果没有,alreadyAdded 为 false,直接在 ArrayList<Fragment> 中 add 添加
- 若有 mContainerId 相同,分两种情况:
- 有相同实例时,移除 mOps中 的该实例
- 实例不相同时,添加到 mOps 中,同时remove 移除 ArrayList<Fragment> 中旧的 Fragment,再 add 添加新的Fragment
本人水平有限,目前 fragment 源码学习 只能分析到这样了,如果有分析的哪里有问题,麻烦看的小伙伴,进行指正,谢谢!
后面有新的理解,还会修改的。