通过这篇博客,我们能知道以下问题:
Fragment add() 和 replace() 方法差别add() 和 replace() 方法Fragment add() 和 replace() 方法差别add() 是添加 Fragment 到容器布局中,再搭配事务对象(FragmentTransaction)的 show() 和 hide() 方法来显示和隐藏 Fragment,replace()顾名思义“替换”,会销毁布局容器内的已有 Fragment,然后重新创建一个新的 Fragment 显示到布局容器中。通过一下两种方式的生命周期方式对比:


我们可以发现,replace() 方式替换的,被替换的Fragment会被彻底销毁,新的会重新创建,每一次的加载到界面到从界面消失的过程都是一个完整的生命周期;而 add() 结合 show()/hide() 方式的Fragment,切换时并不会彻底销毁 Fragment,而是走了 onHiddenChanged() 回调方法,改变了对用户的可见性,同时注意,这个方法并不是生命周期方法。他是在FragmentTransaction调用 show() 和 hide() 方法时才会回调的方法。
注意:如果将Fragment加入回退栈

======================= 回退栈的使用说明 =======================
private fun changeShowFragment(showFragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.fl_content, showFragment)
// 将Fragment增加到回退栈,并指定回退栈的名称为 replaceFragment
.addToBackStack("replaceFragment")
.commitAllowingStateLoss()
}
// 重写返回按钮
override fun handlerOnBack() {
// 判断回退栈是否有未出栈的Fragment
if(supportFragmentManager.backStackEntryCount > 1){
supportFragmentManager.popBackStack()
}else {
super.handlerOnBack()
}
}
======================= 回退栈的使用说明 结束 =======================
add() 和 replace() 方法上面从打印结果知道了 add() 和 replace() 的差别,这一小节,我们从源码的角度来分析、查看系统对各个方法的调用过程。
说明:源码版本是 AndroidX库,具体 androidx.fragment:1.3.4 版本
FragmentManager 和 FragmentTransaction 的获取FragmentActivity 中获取 FragmentManager 和 FragmentTransaction
首先看看 FragmentManager 和 FragmentTransaction 的获取
在 FragmentActivity 中获取 FragmentManager
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
@NonNull
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
通过 mFragments 获取, mFragments 就是 FragmentController,也是 FragmentActivity 的一个成员变量,通过静态方法创建,并且传递了 HostCallbacks 对象(注意:HostCallbacks extends FragmentHostCallback,HostCallbacks继承至FragmentHostCallback),这个对象就是后面会使用到的 mHost。具体创建过程如下:
@NonNull
public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) {
return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
}
// 构造私有,通过静态方法创建,并且为 mHost 赋值
private FragmentController(FragmentHostCallback<?> callbacks) {
mHost = callbacks;
}
FragmentActivity中调用 getSupportFragmentManager() 方法,具体是调用 mFragments.getSupportFragmentManager() 方法。所以我们看一下 FragmentController (上面说过了 mFragments 就是 FragmentController)的 getSupportFragmentManager() 方法:
@NonNull
public FragmentManager getSupportFragmentManager() {
return mHost.mFragmentManager;
}
直接取的是 mHost的变量 mFragmentManager,mHost 就是前面传递进来的 HostCallbacks 对象,那么我们看看这个变量是什么:
final FragmentManager mFragmentManager = new FragmentManagerImpl();
这个变量实际上是在 FragmentHostCallback 定义的,前面我们说过 HostCallbacks 继承至 FragmentHostCallback ,取到 FragmentHostCallback 类中是没有问题的。看名字应该是一个 FragmentManager 的实现类。具体定义如下:
class FragmentManagerImpl extends FragmentManager {
}
没错,确实是继承 FragmentManager ,但是类中没有任何具体的方法,FragmentManager 本身是抽象的,不能创建对象,所以通过一个实现类来创建实例。
在 FragmentActivity 中获取 FragmentTransaction
通过以上分析,我们知道了在Activity中获取到的 FragmentManager 就是 FragmentManagerImpl 对象,那么我们在来看看 FragmentTransaction 是怎样取的,他是通过 FragmentManager 取的,也就是 FragmentManagerImpl 对象调用beginTransaction() 方法的返回值,通过前面我们知道这个方法实际上是在 FragmentManager 实现的,所以看看 FragmentManager 中的 beginTransaction() 方法:
@NonNull
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
很简单,直接创建了一个 BackStackRecord 对象返回,看看 BackStackRecord 类的定义:
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManager.OpGenerator
到了这里,FragmentActivity 中获取 FragmentManager 和 FragmentTransaction 的过程已经知道了,那么接下来看看在 Fragment 中获取 FragmentManager 和 FragmentTransaction 的过程。
Fragment 中获取 FragmentManager 和 FragmentTransaction
在 Fragment 中获取 FragmentManager
在 Fragment 中获取 FragmentManager,是通过 getChildFragmentManager() 方法,看一下 Fragment 中这个方法的定义:
@NonNull
final public FragmentManager getChildFragmentManager() {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " has not been attached yet.");
}
return mChildFragmentManager;
}
直接返回了 mChildFragmentManager 对象,看看这个对象的定义:
FragmentManager mChildFragmentManager = new FragmentManagerImpl();
直接创建了 FragmentManagerImpl 对象,赋值给父类 FragmentManager,这个类我们前面已经说过了。
在 Fragment 中获取 FragmentTransaction
既然FragmentManager对象是 FragmentManagerImpl ,和 FragmentActivity 中一样,那么通过 FragmentManager 获取FragmentTransaction也就是相同的了,这里就不再重复了。
通过前面的了解,我们知道了 FragmentController 中的 mHost 就是在创建 FragmentController 时传递进来的 HostCallbacks 对象,我们现在来看看 Fragment 中的 mHost 是在哪里定义的。
通过查看源码,我们发现 Fragment 中的 mHost 的赋值位置是在 FragmentLayoutInflaterFactory 类的 onCreateView() 方法中,这个方法被调用的过程是:
FragmentActivity#onCreateView() -> FragmentActivity#dispatchFragmentsOnCreateView() -> FragmentController#onCreateView() -> FragmentLayoutInflaterFactory#onCreateView()
在 FragmentLayoutInflaterFactory#onCreateView() 中有这样一行代码:
fragment.mHost = mFragmentManager.getHost();
其中的 mFragmentManager 是在构造方法中传递进来的
FragmentLayoutInflaterFactory(FragmentManager fragmentManager) {
mFragmentManager = fragmentManager;
}
FragmentLayoutInflaterFactory 的创建位置在 FragmentManager 中:
private final FragmentLayoutInflaterFactory mLayoutInflaterFactory =
new FragmentLayoutInflaterFactory(this);
作为 FragmentManager 的一个全局成员变量,并且将 this 传递给 FragmentLayoutInflaterFactory,所以最终 Fragment 中的 mHost 就是 FragmentManager 中的 getHost() 方法返回值:
@NonNull
FragmentHostCallback<?> getHost() {
return mHost;
}
FragmentManager 中的 mHost 赋值位置在 FragmentManager 中的 attachController() 方法中(篇幅原因,同时方法中的内容不是这里关注的重点,具体方法内容就不放进来了),而这个方法的调用位置有两个:一个是 Activity 中调用,一个在 Fragment 中调用,这也是很好理解的,因为Fragment中还可以嵌套Fragment,那么子Fragment中的 mHost 从哪里来了,就需要从父Fragment中来了。具体的调用流程如下:
FragmentActivity()构造 -> FragmentActivity#init() -> FragmentController#attachHost() -> FragmentManager#attachController()
Fragment#performAttach()(该方法中还会调用Fragment的onAttach()方法,它的调用位置就不再这里说了) -> FragmentManager#attachController()
在Activity的调用流程中,传递的就是在 FragmentActivity 中创建的 HostCallbacks 对象,在 Fragment 中保存;在 Fragment的调用流程中,Fragment#performAttach() 方法传递了当前的成员变量 mHost,而这个变量值就是 Activity 中传递过来的。 也就是说,就是在同一个 Activity 中的所有 Fragment 都是共用一个 FragmentController 。
到这里,我们就将 add()、replace() 过程的使用到的部分类有了一定的了解了,接下来就看看这两种方式的具体流程。
add()-> show()/hide()
supportFragmentManager.beginTransaction()
.add(R.id.fl_content, addFragment1)
.add(R.id.fl_content, addFragment2)
.commitAllowingStateLoss()
supportFragmentManager.beginTransaction()
.show(showFragment)
.hide(hideFragment)
.commitAllowingStateLoss()
add() 方法是属于 FragmentTransaction 类,我们看一下具体的代码,其中有多个重载方法
@NonNull
public final FragmentTransaction add(@NonNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle args, @Nullable String tag) {
// 调用 createFragment() 方法,创建Fragment实例对象,然后调用重载方法
return add(createFragment(fragmentClass, args), tag);
}
@NonNull
public FragmentTransaction add(@NonNull Fragment fragment, @Nullable String tag) {
// 调用 doAddOp() 方法,传递 OP_ADD 参数
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
@NonNull
public final FragmentTransaction add(@IdRes int containerViewId,
@NonNull Class<? extends Fragment> fragmentClass, @Nullable Bundle args) {
// 调用重载方法
return add(containerViewId, createFragment(fragmentClass, args));
}
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
// 调用 doAddOp() 方法,传递 OP_ADD 参数
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
@NonNull
public final FragmentTransaction add(@IdRes int containerViewId,
@NonNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle args, @Nullable String tag) {
// 调用重载方法
return add(containerViewId, createFragment(fragmentClass, args), tag);
}
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
// 调用 doAddOp() 方法,传递 OP_ADD 参数
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
FragmentTransaction add(@NonNull ViewGroup container, @NonNull Fragment fragment,
@Nullable String tag) {
fragment.mContainer = container;
// 调用重载方法
return add(container.getId(), fragment, tag);
}
======================= 针对上面 add() 方法的一个说明:=======================
以上就是 add() 的各个方法,他们最终调用的都是 doAddOp() 方法,并且传递了容器id,Fragment对象,tag 和 OP_ADD 参数(重点注意OP_ADD参数,)
createFragment() 方法的作用是根据 Fragment 的 Class 创建对象
@NonNull
private Fragment createFragment(@NonNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle args) {
// 调用 FragmentFactory#instantiate() 方法
Fragment fragment = mFragmentFactory.instantiate(mClassLoader, fragmentClass.getName());
if (args != null) {
// 设置参数
fragment.setArguments(args);
}
return fragment;
}
FragmentFactory#instantiate() 方法
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
try {
// 通过反射创建对象,Fragment 必须有无参构造,否则会报错
Class<? extends Fragment> cls = loadFragmentClass(classLoader, className);
return cls.getConstructor().newInstance();
} catch (Exception e) {
}
}
======================= 针对上面 add() 方法的一个说明结束 =======================
继续查看 FragmentTransaction#doAddOp() 方法
// FragmentTransaction#doAddOp()
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
final Class<?> fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
// 省略对参数进行判断
// 调用 addOp() 方法,这里的 opcmd 参数实际上就是 OP_ADD
addOp(new Op(opcmd, fragment));
}
// FragmentTransaction#addOp()
void addOp(Op op) {
mOps.add(op);
op.mEnterAnim = mEnterAnim;
op.mExitAnim = mExitAnim;
op.mPopEnterAnim = mPopEnterAnim;
op.mPopExitAnim = mPopExitAnim;
}
Op 类的定义
static final class Op {
int mCmd; // 操作类型 可选有:OP_NULL|OP_ADD|OP_REPLACE|OP_REMOVE|OP_HIDE|OP_SHOW|OP_DETACH|OP_ATTACH
Fragment mFragment; // 操作的Fragment对象
int mEnterAnim; // 如此动画
int mExitAnim; // 退出动画
int mPopEnterAnim; // 弹入动画
int mPopExitAnim; // 弹出动画
Lifecycle.State mOldMaxState; // 老的生命周期状态值
Lifecycle.State mCurrentMaxState; // 当前的生命周期状态值(当前需要改变成那种状态)
Op(int cmd, Fragment fragment) {
this.mCmd = cmd;
this.mFragment = fragment;
this.mOldMaxState = Lifecycle.State.RESUMED;
this.mCurrentMaxState = Lifecycle.State.RESUMED;
}
}
这两个方法比较简单,就是对参数进行判断,然后创建一个 Op 对象(注意创建对象的第一个参数实际就是 OP_ADD),将参数和对象保存进去,然后将整个 Op 对象添加到 FragmentTransaction#mOps 列表中。接下来就看 show()/hide() 方法的调用过程
show()/hide() 方法:// FragmentTransaction#show() 方法
@NonNull
public FragmentTransaction show(@NonNull Fragment fragment) {
// 调用 addOp() 方法,传递 OP_SHOW 参数
addOp(new Op(OP_SHOW, fragment));
return this;
}
// FragmentTransaction#show() 方法
@NonNull
public FragmentTransaction hide(@NonNull Fragment fragment) {
// 调用 addOp() 方法,传递 OP_HIDE 参数
addOp(new Op(OP_HIDE, fragment));
return this;
}
这里都是直接调用了 addOp() 方法,只是在创建 Op 对象时传递了不同的参数, add() 方法传递的是 OP_ADD,show() 方法传递的是 OP_SHOW, hide() 方法传递的是 OP_HIDE
通过上面我们发现,这几个方法并没有什么实际的操作,仅仅是检查参数和保存数据,那么他们真正的操作应该就是在 FragmentTransaction#commit() 方法上了,这个方法我们之后在看,因为 add()、 show() 、 hide() 方法都是保存数据,那我们先来看看 replace() 方法是不是也是只保存了需要操作的数据了。
replace()同样,replace() 方法是属于 FragmentTransaction 类,我们看一下具体的代码
@NonNull
public final FragmentTransaction replace(@IdRes int containerViewId,
@NonNull Class<? extends Fragment> fragmentClass, @Nullable Bundle args) {
return replace(containerViewId, fragmentClass, args, null);
}
@NonNull
public final FragmentTransaction replace(@IdRes int containerViewId,
@NonNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle args, @Nullable String tag) {
return replace(containerViewId, createFragment(fragmentClass, args), tag);
}
@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment) {
return replace(containerViewId, fragment, null);
}
@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
同样有多个重载方法,但是多个重载方法最终调用的也是 doAddOp() 方法,与我们上面的猜想一样,只是传递的参数是 OP_REPLACE,与上面的对应也是关系类似。
到了这一步,我们就发现了,add()、 show() 、 hide() 以及replace()方法都是创建了一个 Op 对象,并保存到 FragmentTransaction#mOps 列表中去,实际操作应该是都是在 FragmentTransaction#commit() 方法中了,我们接着就来看看这个方法。
commit() 方法有多个类似的,我们这里看看常用的 commit() 、 commitAllowingStateLoss() 两个,其实其他的几个也一样,他们最终调用的方法都是一样的,明白了这两个,其他的也就明白了。需要注意的是,这些方法在 FragmentTransaction 类中是抽象的,具体的实现在
BackStackRecord 类中,通过上面的分析我们也知道了,beginTransaction() 方法获取的就是 BackStackRecord 类对象。
BackStackRecord 类中的这两个方法具体实现如下:
@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
都是调用了 commitInternal() 方法,只是参数不一样,一个 false,一个 true,这个参数表示是否允许丢失状态信息,具体的作用我们通过源码继续了解。接着往下继续看 BackStackRecord#commitInternal() 方法
int commitInternal(boolean allowStateLoss) {
// 防止重复提交
if (mCommitted) throw new IllegalStateException("commit already called");
// 省略打印日志代码
// 修改状态,正在提交
mCommitted = true;
// 判断是否需要保存到回退栈,调用了 addToBackStack() 方法才会为 true
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex();
} else {
mIndex = -1;
}
// 将操作放入到队列中,调用的是 FragmentManager 类中的方法
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
FragmentManager#enqueueAction() 方法:
void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
// 是否允许丢失状态,如果不允许,就需要对状态进行检查,检查不通过抛出异常
if (!allowStateLoss) {
if (mHost == null) {
if (mDestroyed) {
throw new IllegalStateException("FragmentManager has been destroyed");
} else {
throw new IllegalStateException("FragmentManager has not been attached to a "
+ "host.");
}
}
checkStateLoss();
}
synchronized (mPendingActions) {
// mHost 是在 FragmentActivity 中创建,然后传递过来的,如果为 null,表示Fragment的容器布局对应的页面不存在了
if (mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
// 把操作添加到 mPendingActions 中
mPendingActions.add(action);
// 继续调用方法
scheduleCommit();
}
}
FragmentManager#scheduleCommit() 方法:
void scheduleCommit() {
synchronized (mPendingActions) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions.size() == 1;
// 判断是否有已经准备好的,需要执行的任务
if (postponeReady || pendingReady) {
// 先移除已有的消息,然后再通过Handler发送消息开始处理
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
// 更新返回状态是否启用,根据回退栈中的Fragment类确定
updateOnBackPressedCallbackEnabled();
}
}
}
主要来看FragmentManager#Handler 需要执行的任务 mExecCommit 做了什么
private Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions(true);
}
};
boolean execPendingActions(boolean allowStateLoss) {
// 执行之前已推迟但现在已准备就绪的事务
ensureExecReady(allowStateLoss);
boolean didSomething = false;
// 1. 根据事务对象生成待执行的操作,并添加到记录中
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
// 2. 移除多余的操作并执行
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
} finally {
// 清空缓存队列数据
cleanupExec();
}
didSomething = true;
}
updateOnBackPressedCallbackEnabled();
doPendingDeferredStart();
mFragmentStore.burpActive();
return didSomething;
}
我们一个一个方法来看:
首先看一下 FragmentManager#generateOpsForPendingActions() 方法
// FragmentManager#generateOpsForPendingActions()
private boolean generateOpsForPendingActions(@NonNull ArrayList<BackStackRecord> records,
@NonNull ArrayList<Boolean> isPop) {
boolean didSomething = false;
synchronized (mPendingActions) {
if (mPendingActions.isEmpty()) {
return false;
}
final int numActions = mPendingActions.size();
for (int i = 0; i < numActions; i++) {
didSomething |= mPendingActions.get(i).generateOps(records, isPop);
}
// 清空 mPendingActions 列表
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
}
return didSomething;
}
在这个方法中遍历 mPendingActions 调用元素(其中的元素是在 FragmentManager#enqueueAction() 方法中添加的 BackStackRecord 对象)的 generateOps() 方法,我们看一下这个方法:
// BackStackRecord#generateOps() 方法
@Override
public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
@NonNull ArrayList<Boolean> isRecordPop) {
records.add(this);
isRecordPop.add(false);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
return true;
}
将当前对象加入到 records 中,实际上也就是 FragmentManager#mTmpRecords 列表,并且给FragmentManager#mTmpIsPop 增加了false 元素,最后还根据是否需要加入到回退栈进行处理了。
在 generateOpsForPendingActions() 中,执行完 generateOps() 方法后就清空了 FragmentManager#mPendingActions 列表。这个方法到这里就差不多了。我们回头接着看另外一个方法。
查看另一个 FragmentManager#removeRedundantOperationsAndExecute() 方法:
// FragmentManager#removeRedundantOperationsAndExecute()
private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records,
@NonNull ArrayList<Boolean> isRecordPop) {
// ...
if (startIndex != numRecords) {
executeOpsTogether(records, isRecordPop, startIndex, numRecords);
}
}
接着看 FragmentManager#executeOpsTogether() 方法:
// FragmentManager#executeOpsTogether()
private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records,
@NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
boolean addToBackStack = false;
if (mTmpAddedFragments == null) {
mTmpAddedFragments = new ArrayList<>();
} else {
mTmpAddedFragments.clear();
}
mTmpAddedFragments.addAll(mFragmentStore.getFragments());
Fragment oldPrimaryNav = getPrimaryNavigationFragment();
for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
final BackStackRecord record = records.get(recordNum);
final boolean isPop = isRecordPop.get(recordNum);
if (!isPop) {
// 1. 不是出栈,走这里
oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
} else {
oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
}
addToBackStack = addToBackStack || record.mAddToBackStack;
}
mTmpAddedFragments.clear();
// ...
// 2. 展开操作
executeOps(records, isRecordPop, startIndex, endIndex);
// ...
// 根据是否加入回退栈进行处理
if (addToBackStack) {
reportBackStackChanged();
}
}
这里把不是特别重要的代码去掉了,主要注意两步:
record.expandOps(mTmpAddedFragments, oldPrimaryNav):这一步主要是操作展开(第二个参数与AndroidX导航组件相关,这里不管他)。比如说一个replace操作,需要把之前的Fragment的移除掉和添加新的Fragment的操作,并把新的操作对象Op添加到mOps列表中,这个列表我们在上面也看到过,不管是add还是replace都会调用 FragmentTransaction#addOp() 方法将一个 Op 对象添加到 mOps 中。看一下 OP_REPLACE 操作的代码
case OP_REPLACE: {
final Fragment f = op.mFragment;
final int containerId = f.mContainerId;
boolean alreadyAdded = false;
for (int i = added.size() - 1; i >= 0; i--) {
final Fragment old = added.get(i);
if (old.mContainerId == containerId) {
if (old == f) {
alreadyAdded = true;
} else {
// 先移除老的Fragment操作,构建 Op 对象,保存到 mOps 列表
final Op removeOp = new Op(OP_REMOVE, old);
removeOp.mEnterAnim = op.mEnterAnim;
removeOp.mPopEnterAnim = op.mPopEnterAnim;
removeOp.mExitAnim = op.mExitAnim;
removeOp.mPopExitAnim = op.mPopExitAnim;
mOps.add(opNum, removeOp);
added.remove(old);
opNum++;
}
}
}
if (alreadyAdded) {
// 如果已经存在,多余操作,删除
mOps.remove(opNum);
opNum--;
} else {
// 没有,将操作改为 OP_ADD
op.mCmd = OP_ADD;
added.add(f);
}
}
对于replace操作,先是判断了是否已经存在(防止重复添加,如果是重复添加,这一步就是多余的,直接移除此次操作);如果不存在,将先将需要被替换的 Fragment 移除掉(也是构建一个 Op 对象,指定 OP_REMOVE (移除)操作),然后将新的 Fragment 操作由 OP_REPLACE 修改为 OP_ADD:新增。
接下来看一下另一个 FragmentManager#executeOps(records, isRecordPop, startIndex, endIndex) 方法
FragmentManager#executeOps(records, isRecordPop, startIndex, endIndex)
private static void executeOps(@NonNull ArrayList<BackStackRecord> records,
@NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
final BackStackRecord record = records.get(i);
final boolean isPop = isRecordPop.get(i);
if (isPop) {
record.bumpBackStackNesting(-1);
boolean moveToState = i == (endIndex - 1);
record.executePopOps(moveToState);
} else {
record.bumpBackStackNesting(1);
// 不是出栈操作,走else到这里
record.executeOps();
}
}
}
继续查看 BackStackRecord#expandOps() 方法:
void executeOps() {
final int numOps = mOps.size();
// 遍历操作列表,进行操作
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.mFragment;
if (f != null) {
f.setPopDirection(false);
f.setNextTransition(mTransition);
f.setSharedElementNames(mSharedElementSourceNames, mSharedElementTargetNames);
}
// 1. 根据操作类型进行对应操作,我们主要看 OP_ADD,因为不管是 add 还是 replace 都是走的这一步,其他的操作我们就不一个一个看了。
switch (op.mCmd) {
case OP_ADD:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.addFragment(f);
break;
case OP_REMOVE:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.removeFragment(f);
break;
case OP_HIDE:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.hideFragment(f);
break;
case OP_SHOW:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.showFragment(f);
break;
case OP_DETACH:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.detachFragment(f);
break;
case OP_ATTACH:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.attachFragment(f);
break;
case OP_SET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(f);
break;
case OP_UNSET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(null);
break;
case OP_SET_MAX_LIFECYCLE:
mManager.setMaxLifecycle(f, op.mCurrentMaxState);
break;
default:
throw new IllegalArgumentException("Unknown cmd: " + op.mCmd);
}
// 2. 如果不是add操作,就会走这里
if (!mReorderingAllowed && op.mCmd != OP_ADD && f != null) {
if (!FragmentManager.USE_STATE_MANAGER) {
mManager.moveFragmentToExpectedState(f);
}
}
}
// 3. 最终都会走到这里来
if (!mReorderingAllowed && !FragmentManager.USE_STATE_MANAGER) {
// Added fragments are added at the end to comply with prior behavior.
mManager.moveToState(mManager.mCurState, true);
}
}
这个方法可以分为三步:
1. 遍历mOps操作列表,根据操作类型进行对应操作
2. 如果不是add操作,就调用 FragmentManager.moveFragmentToExpectedState(f)
3. 调用 FragmentManager.moveToState(int newState, boolean always)
我们一步一步看,首先第一步,case 中调用 FragmentManager#addFragment() 方法
case OP_ADD:
f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
mManager.setExitAnimationOrder(f, false);
mManager.addFragment(f);
break;
接着往下走 FragmentManager#addFragment() 方法
FragmentStateManager addFragment(@NonNull Fragment fragment) {
if (isLoggingEnabled(Log.VERBOSE)) Log.v(TAG, "add: " + fragment);
FragmentStateManager fragmentStateManager = createOrGetFragmentStateManager(fragment);
fragment.mFragmentManager = this;
// 调用 FragmentStore#makeActive() 方法,将 Fragment 添加到 mActive 中
mFragmentStore.makeActive(fragmentStateManager);
if (!fragment.mDetached) {
// 调用 FragmentStore#addFragment() 方法,将 Fragment 添加到 mAdded 中
mFragmentStore.addFragment(fragment);
fragment.mRemoving = false;
if (fragment.mView == null) {
fragment.mHiddenChanged = false;
}
if (isMenuAvailable(fragment)) {
mNeedMenuInvalidate = true;
}
}
return fragmentStateManager;
}
这个方法主要就是将该Fragment添加到mAdded中和mActive中,具体代码比较简单,就不放出来了。
第二步主要是将 mOps 中的所有对应的 Fragment 设置到对应的状态,并且调用回调,这个第二步调用的方法在后面第三步中也会调用到,就在后面放一起说明。
接着我们跳过第二步,直接看第三步调用 FragmentManager.moveToState(int newState, boolean always) 方法
void moveToState(int newState, boolean always) {
if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
// always:表示是否需要强制更新状态,即使状态一样,否则的话,状态一样时不更新
if (!always && newState == mCurState) {
return;
}
mCurState = newState;
if (USE_STATE_MANAGER) {
// 使用状态管理,(不管是否使用状态管理,最后都是调用的同样的方法,只是在中间过程有些不一样)
mFragmentStore.moveToExpectedState();
} else {
// 不使用状态管理,在之前,我们已经将相关操作添加到这两个列表中了
// mFragmentStore.getFragments() 方法返回的就是 mAdded 列表
for (Fragment f : mFragmentStore.getFragments()) {
moveFragmentToExpectedState(f);
}
// mFragmentStore.getActiveFragmentStateManagers() 方法返回的就是 mActive 列表
for (FragmentStateManager fragmentStateManager :
mFragmentStore.getActiveFragmentStateManagers()) {
Fragment f = fragmentStateManager.getFragment();
if (!f.mIsNewlyAdded) {
moveFragmentToExpectedState(f);
}
boolean beingRemoved = f.mRemoving && !f.isInBackStack();
if (beingRemoved) {
mFragmentStore.makeInactive(fragmentStateManager);
}
}
}
startPendingDeferredFragments();
if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
mHost.onSupportInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
}
}
我们分别看一下这两个的调用过程
使用状态管理:直接调用 FragmentStateManager#moveToExpectedState() 方法
// FragmentStateManager#moveToExpectedState()
void moveToExpectedState() {
try {
mMovingToState = true;
int newState;
// 根据状态调用对应的方法 attach()/create()/createView()/...
while ((newState = computeExpectedState()) != mFragment.mState) {
if (newState > mFragment.mState) {
// Moving upward
int nextStep = mFragment.mState + 1;
switch (nextStep) {
case Fragment.ATTACHED:
attach();
break;
case Fragment.CREATED:
create();
break;
case Fragment.VIEW_CREATED:
ensureInflatedView();
createView();
break;
case Fragment.AWAITING_EXIT_EFFECTS:
activityCreated();
break;
case Fragment.ACTIVITY_CREATED:
if (mFragment.mView != null && mFragment.mContainer != null) {
SpecialEffectsController controller = SpecialEffectsController
.getOrCreateController(mFragment.mContainer,
mFragment.getParentFragmentManager());
int visibility = mFragment.mView.getVisibility();
SpecialEffectsController.Operation.State finalState =
SpecialEffectsController.Operation.State.from(visibility);
controller.enqueueAdd(finalState, this);
}
mFragment.mState = Fragment.ACTIVITY_CREATED;
break;
case Fragment.STARTED:
start();
break;
case Fragment.AWAITING_ENTER_EFFECTS:
mFragment.mState = Fragment.AWAITING_ENTER_EFFECTS;
break;
case Fragment.RESUMED:
resume();
break;
}
} else {
// Moving downward
int nextStep = mFragment.mState - 1;
switch (nextStep) {
case Fragment.AWAITING_ENTER_EFFECTS:
pause();
break;
case Fragment.STARTED:
mFragment.mState = Fragment.STARTED;
break;
case Fragment.ACTIVITY_CREATED:
stop();
break;
case Fragment.AWAITING_EXIT_EFFECTS:
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "movefrom ACTIVITY_CREATED: " + mFragment);
}
if (mFragment.mView != null) {
// Need to save the current view state if not done already
// by saveInstanceState()
if (mFragment.mSavedViewState == null) {
saveViewState();
}
}
if (mFragment.mView != null && mFragment.mContainer != null) {
SpecialEffectsController controller = SpecialEffectsController
.getOrCreateController(mFragment.mContainer,
mFragment.getParentFragmentManager());
controller.enqueueRemove(this);
}
mFragment.mState = Fragment.AWAITING_EXIT_EFFECTS;
break;
case Fragment.VIEW_CREATED:
mFragment.mInLayout = false;
mFragment.mState = Fragment.VIEW_CREATED;
break;
case Fragment.CREATED:
destroyFragmentView();
mFragment.mState = Fragment.CREATED;
break;
case Fragment.ATTACHED:
destroy();
break;
case Fragment.INITIALIZING:
detach();
break;
}
}
}
// 根据需要回调 Fragment#onHiddenChanged() 方法
if (FragmentManager.USE_STATE_MANAGER && mFragment.mHiddenChanged) {
if (mFragment.mView != null && mFragment.mContainer != null) {
// Get the controller and enqueue the show/hide
SpecialEffectsController controller = SpecialEffectsController
.getOrCreateController(mFragment.mContainer,
mFragment.getParentFragmentManager());
if (mFragment.mHidden) {
controller.enqueueHide(this);
} else {
controller.enqueueShow(this);
}
}
if (mFragment.mFragmentManager != null) {
mFragment.mFragmentManager.invalidateMenuForFragment(mFragment);
}
mFragment.mHiddenChanged = false;
mFragment.onHiddenChanged(mFragment.mHidden);
}
} finally {
mMovingToState = false;
}
}
不使用状态管理:FragmentManager#moveFragmentToExpectedState(f)(这个方法也就是上面非add操作时先会调用的方法,这里就一起看了) -> FragmentManager#moveToState(@NonNull Fragment f) -> FragmentManager#moveToState(@NonNull Fragment f, int newState) -> FragmentManager#moveToState(@NonNull Fragment f, int newState) -> 根据状态调用 FragmentStateManager#attach()/create()/createView()/activityCreated()/.../destroy()/detach() 等方法,和使用状态管理调用同样的方法。
最后,通过调用FragmentStateManager#attach()/create()/createView()/activityCreated()/.../destroy()/detach() 等方法最终会回调 Fragment 的对应生命周期方法。
Fragment 的 View 加载到容器中的过程将 Fragment#createView() 方法的 View 加载到具体的页面上在 FragmentStateManager#createView() 方法中实现:
在该方法(FragmentStateManager#createView())中调用 Fragment#performCreateView() 方法进而调用 Fragment#onCreateView() 方法创建View 并赋值给 Fragment 的成员变量 mView
在该方法(FragmentStateManager#createView())中接着调用 FragmentStateManager#addViewToContainer() 方法
void addViewToContainer() {
int index = mFragmentStore.findFragmentIndexInContainer(mFragment);
mFragment.mContainer.addView(mFragment.mView, index);
}
通过调用 addView() 方法将 View 增加到容器中,mContainer 就是加载 Fragment 的容器。
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
除了协议(protocol)定义中的斜杠('http[s]://'、'ftp://'等)之外,我想替换URL中的所有多个斜杠。我该怎么做?此代码无一异常(exception)地替换:url.gsub(/\/\/+/,'/') 最佳答案 您只需排除任何以:开头的匹配项url.gsub(/([^:])\/\//,'\1/') 关于ruby正则表达式:replacedoubleslashesinURL,我们在StackOverflow上找到一个类似的问题: http
我在事件管理员编辑页面中有嵌套资源,但我只想允许管理员编辑现有资源的内容,而不是添加新的嵌套资源。我的代码看起来像这样:formdo|f|f.inputsdof.input:authorf.input:contentf.has_many:commentsdo|comment_form|comment_form.input:contentcomment_form.input:_destroy,as::boolean,required:false,label:'Remove'endendf.actionsend但它在输入下添加了“添加新评论”按钮。我怎样才能禁用它,并只为主窗体保留f.ac
我已经开始学习Ruby,我已经阅读了一些教程,甚至还买了一本书(“ProgrammingRuby1.9-ThePragmaticProgrammers'Guide”),我遇到了一些以前从未见过的新东西使用我知道的任何其他语言(我是一名PHP网络开发人员)。block和过程。我想我明白它们是什么,但我不明白的是为什么它们如此伟大,以及我应该在何时何地使用它们。我到处都看到他们说block和过程是Ruby中的一个很棒的特性,但我不理解它们。这里有人能给像我这样的Ruby新手一些解释吗? 最佳答案 block有很多好处。电梯演讲:bloc
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍 介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。 内容有: ①:Hub模型的方法介绍 ②:服务器端代码介绍 ③:前端vue3安装并调用后端方法 ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke() 去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on
rails中View的解析过程是怎样的?我对View中erb标记中原始html与ruby代码的解析顺序部分感兴趣。我认为这是View代码被解析并最终发送给请求者的顺序:Controller调用ViewView代码从上到下解析当Rails在解析过程中遇到erb标记时:rails解析它并将结果附加到解析的html(这包括erb标签引用助手)一旦整个View被解析,整体结果将发送给请求者这似乎并非如此。看来View代码会扫描任何erb片段并首先解析那些片段(包括对助手的引用)。之后,rails然后从上到下解析所有View代码并将结果发送给请求者。以这个View为例:#_form.html
为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti
快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言 目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u
TCP是面向连接的协议,连接的建立和释放是每一次面向连接的通信中必不可少的过程。TCP连接的管理就是使连接的建立和释放都能正常地进行。三次握手TCP连接的建立—三次握手建立TCP连接①若主机A中运行了一个客户进程,当它需要主机B的服务时,就发起TCP连接请求,并在所发送的分段中用SYN=1表示连接请求,并产生一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x。主机B收到A的连接请求报文,就完成了第一次握手。客户端发送SYN=1表示连接请求客户端发送一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x②主机B如果同意建立连接,则向主机A发送确认报