写点什么

Fragment add 与 replace 的区别 (1)(1)

用户头像
Android架构
关注
发布于: 20 小时前
  1. 如果FragmentA通过 replace 操作添加的,在将FragmentA替换为FragmentB时,使用 replace 替换


  • 通过 add 添加的 Fragments,可以由开发者自由控制当前应该显示哪个 Fragment,以及隐藏哪个 Fragment,其实在源码中就是对应 Fragment 中 View 的显示和隐藏。

  • 通过 replace 添加 的 Fragment,当前已存在的 Fragment 被替换时走生命周期销毁流程,传递给 replace 方法的 Fragment 走生命周期创建流程。

  • 根据以上结论,我们可以得到一个实例开发中添加 Fragment 时很重要的一个结论

  • 当 Fragment 不可见时,如果你要保留 Fragment 中的数据以及 View 的显示状态,那么可以使用 add 操作,后续中针对不同的状态隐藏和显示不同的 Fragment。

  • 优点:快,知识 Fragment 中 View 的显示和隐藏

  • 缺点:内存中保留的数据太多,容易导致造成 OOM 的风险。

  • 当 Fragment 不可见时,你不需要保留 Fragment 中的数据以及 View 的显示状态,那么可以使用 replace。

  • 优点:节省内存,不需要的数据能立即释放掉

  • 缺点:频繁创建 Fragment,也就是频繁走 Fragment 生命周期创建和销毁流程,造成性能开销。


  1. 然后分别对进行一个 add 和 replace 事物操作时,源码中是如何处理的。


要对 add 和 replace 事务操作源码中是如何处理的,那么先看下对 Fragment 事务操作提交流程。


简要流程图



点击查看大图


从流程图中发现当执行第 10 步骤,也就是当调用 BackStackRecord#expandOps 方法时,对 replace 操作会进行修正


//BackStackRecord#expandOpsFragment 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);}case OP_REPLACE: {final Fragment f = op.fragment;final int containerId = f.mContainerId;boolean alreadyAdded = false;for (int i = added.size() - 1; i >= 0; i--) {//如果当前已有 Fragment 添加到 Activity 中,取出已添加的 Fragmentfinal Fragment old = added.get(i);if (old.mContainerId == containerId) {if (old == f) {//如果 replace 的是同一个 FragmentalreadyAdded = true;} else {//不是同一 Fragment,删除旧的 Fragmentfinal 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) {//从操作序列中移除当前请求的操作,表示当前操作无效 mOps.remove(opNum);opNum--;} else {//当前 Activity 中同一个 id 没有添加 Fragment 或者同一个 id 存在 Fragment,但 replace 传递 fragment 和已有的不是同一个实例,将操作类型 replace 修改为 addop.cmd = OP_ADD;added.add(f);}}break;}}return oldPrimaryNav;}


由上面代码分析可知


  1. 如果当前 Activity 同一个 id 还没有添加 Fragment,replace 操作和 add 操作一样。


即执行两者操作生命周期变化:onAttach->onCreate->onCreateView->onActivityCreated->onStart-onResume,


Fragment 所依附的 Activity 销毁时,执行 onPause->onStop->onDestoryView->onDestory->onDetach


  1. 如果当前 Activity 同一个 id 存在 Fragment,replace 传递的 Fragment 实例和已存在的 Fragment 实例一样,replace 无效果,这点从上面源码中可以看出

  2. 如果当前 Activity 同一个 id 存在 Fragment,replace 传递的 Fragment 实例和已存在的 Fragment 实例不一样,replace 操作会转换为 remove 和 add 操作,即删除旧的 Fragment,添加新的 Fragment,这点从上面代码中也可以看出来。


旧的 Fragment 执行 onPause->onStop->onDestoryView->onDestory->onDetach


新的 Fragment 执行 onAttach->onCreate->onCreateView->onActivityCreated->onStart-onResume


总的来说 BackStackRecord#expandOps 就是修正 replace 的操作,将 replace 变为 remove 和 add 操作。


无论是 remove,add 我们发现操作的都是 mOps 集合。mOps 中存放的是事务操作的序列。根据流程图在接下来第 11 步骤也就是 BackStackRecord#executeOps 会执行 mOps 存放的事务操作。


void executeOps() {final int numOps = mOps.size();for (int opNum = 0; opNum < numOps; opNum++) {final Op op = mOps.get(opNum);final Fragment f = op.fragment;switch (op.cmd) {case OP_ADD:f.setNextAnim(op.enterAnim);mManager.addFragment(f, false);break;case OP_REMOVE:f.setNextAnim(op.exitAnim);mManager.removeFragment(f);break;case OP_HIDE:f.setNextAnim(op.exitAnim);mManager.hideFragment(f);break;case OP_SHOW:f.setNextAnim(op.enterAnim);mManager.showFragment(f);break;


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


case OP_DETACH:f.setNextAnim(op.exitAnim);mManager.detachFragment(f);break;case OP_ATTACH:f.setNextAnim(op.enterAnim);mManager.attachFragment(f);break;case OP_SET_PRIMARY_NAV:mManager.setPrimaryNavigationFragment(f);break;case OP_UNSET_PRIMARY_NAV:mManager.setPrimaryNavigationFragment(null);break;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Fragment add与replace的区别(1)(1)