一、入口
一般我们添加一个 Fragment 的套路是这样的(xml 文件中不常不在我们讨论方法以内):
创建一个 Fragment 实例,获取 FragmentManager,然后通过 FragmentManager 获取去到一个 FragmentTransaction,然后通过 FragmentTransaction 的 add() 方法添加一个 Fragment,最后调用 FragmentTransaction 的 commit() 方法完成整个添加过程。
而在中间的 add() 方法中又有几种不同的方式,如:将当前添加的 Fragment 加入到 Fragment 回退栈中等。这个再跑套路中我们要一一分析,下面就开始吧。
二、开始讨论
Fragment_1 fragment_1 = new Fragment_1();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
1、add() 方法
我们点击进入到 FragmentTransaction 的 add() 方法中,add() 方法有三个,我们选择常用的,第一个参数为 int ,第二个参数为 Fragment 的方法查看,然后发现 FragmentTransaction 是个抽象类,那么我们回退一步,到 FragmentManager 获取 FragmentTransaction 的 beginTransaction() 方法中查看,FragmentManager 是一个接口,而他的实现者则是其一个内部类,FragmentMaanagerImpl ,我们查看其中的 beginTransaction() 方法。
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
这里直接返回了一个 BackStackRecord 实例,从命名上这个类是一个 回退栈的记录类,我们进去看一看,下面是这个类的一个局部代码。
/**
* Entry of an operation on the fragment back stack.
*/
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
static final String TAG = FragmentManagerImpl.TAG;
static final boolean SUPPORTS_TRANSITIONS = Build.VERSION.SDK_INT >= 21;
final FragmentManagerImpl mManager;
......
......
......
这个是 FragmentTrnsaction 的子类,那么我们就清除了,我们再调用 FragmentTransaction 的 add() 方法时,其实调用的是 BackStackRecord 的 add() 方法。add() 方法我们稍后再看,先看这个类的初始化时都做了什么。
2、BackStackRecord 类
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
额,就做了一件事情,引用一个 FragmentManagerImpl 实例。那么我们还是去它的 add() 方法。
3、真 add() 方法
@Override
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
看到三个 add() 方法都是间接的调用了 doAAddOp() 这个方法,名字起的诡异,我们接着看。
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
final Class fragmentClass = fragment.getClass();
// 获取需要添加的 Fragment 类的修饰符
final int modifiers = fragmentClass.getModifiers();
// 如果该 Fragment 类,是匿名内部类、成员内部类、不是静态的、不是公共的都将抛出错误
if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
+ " must be a public static class to be properly recreated from"
+ " instance state.");
}
fragment.mFragmentManager = mManager;
// Fragment 可以重复使用,同一 Fragment 的 tag 不能相同
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;
}
// Fragment 要添加进那个布局
if (containerViewId != 0) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no id");
}
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;
}
addOp(new Op(opcmd, fragment));
}
方法还是比较容易看懂的,就是对要添加的 Fragment 的信息做一些保存,然后调用了 addOp() 方法,接着看。
void addOp(Op op) {
mOps.add(op);
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
}
static final class Op {
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
Op() {
}
Op(int cmd, Fragment fragment) {
this.cmd = cmd;
this.fragment = fragment;
}
}
方法也是比较简单的,将一个 Op 对象添加进入一个 ArrayList 中,而 Op 中保存了启动这个 Fragment 时需要的属性,以及要对这个 Fragment 要做的事情,也就是 Op 对象 cmd 这个属性,这个属性在 BackStackRecord 中是定义好的,需要添加一个 Fragment 就是使用 OP_ADD ,这个源码中以及写好评,我们不能更改的。
到此,add() 方法所做的事情完结,总结下来 add() 方法所做的事情就是将需要添加 Fragment 的信息进行记录,然后将这些信息和要添加的 Fragment 以 Op 的形式添加进一个 ArrayList 中。下面我们接着看 add() 操作之后的另一个方法 commit()。
4、commit() 方法
依然的看 BackStackRecord 类中的 commit() 方法。
@Override
public int commit() {
return commitInternal(false);
}
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);
pw.close();
}
mCommitted = true;
// 如果需要将这一次 Fragment 事务加入回退栈,就将其加入
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
// 调用 FragmentManager 异步执行事务
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
commit() 方法直接调用了 commitInternal() ,commitInternal() 调用 FragmentManager 异步执行事务。
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}
interface OpGenerator {
boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
}
该方法接收的参数时一个 OpGenerator 而 BackStackRecord 实现了这个接口,OpGenerator 中只有一个方法,然后将我们上一步 add() 的事务添加进入待执行队列 mPendingActions 当中,然后调用 scheduleCommit() 方法开始执行提交。
private void scheduleCommit() {
synchronized (this) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
// 执行待执行列的事务
mHost.getHandler().post(mExecCommit);
}
}
}
到此事务执行完毕。
参考:https://xiazdong.github.io/2017/06/15/android-fragment/