理解 FragmentManager 的 BackStack

简介: 最近, QMUI 的 fragment 管理框架升级到了 0.1.0, 添加了一个新的接口 startFragmentAndDestroyCurrent, 可以方便的完成类似 startActivity 之后 finish 的效果,有兴趣的可以试试。

最近, QMUI 的 fragment 管理框架升级到了 0.1.0, 添加了一个新的接口 startFragmentAndDestroyCurrent, 可以方便的完成类似 startActivity 之后 finish 的效果,有兴趣的可以试试:

implementation "com.qmuiteam:arch:0.1.0"

其源码在 QMUI,有兴趣的也可以去看看。

FragmentTransition#addToBackStack 的误用


之前也有人在 issue 中问道如何实现类似 Activity#finish() 的效果,我不加思索的回答到:

FragmentTransition 有提供 addToBackStack(boolean) 方法, 启动前一个 fragment (不是新的那个)时, addToBackStack 传参为 false 就行。

这样初看也确实解决了问题,一切看似完美,直到另一个 issue 的出现。在阅读源码来解决这个问题时,我发现了 addToBackStack(false) 是存在问题的,这篇文章会指出问题所在,然后给出 QMUI 的解决方案。

BackStack 的工作机制


在之前的文章我也有提到, BackStack 并不是将 fragment 保存到堆栈,而是将操作(add, remove等)保存到堆栈,然后在返回时将操作逆着来就行。


在开始之前,先介绍 Fragment 的一个私有成员变量:


Fragment 存在一个成员变量 mBackStackNesting,它是标志 fragment 是否存在于 BackStack 的重要变量,每个 fragment 的每次操作都会影响到它, 只有它的值小于等于0时,fragment 才会走到 onDestroy,从而得到释放


一般情况下,我们切换 fragment 时 BackStack 的变更行为为:

现在,让我们来看看使用 addToBackStack(false) 时会发生什么:

回到之前提到的 issue。其出错的原因是 findCurrentFragment 出错,而 findCurrentFragment 会先在被添加到 FragmentManager 中的 fragment 队列中去寻找,根据上面的描述,addToBackStack(false)会导致页面存在多个 fragment, 所以猜测可能是它导致的问题,但我也不能确定我的猜测 100% 正确,因为我并没能在开发环境中重现。

QMUI 的 fix 方案。


现在来说说 QMUI 的 fix 方案。其实也很简单,我们直接对 BackStack 的最后一个 Entity 做一些修正(以上图为例):


1.修改 Op[addB] 替换为 Op[addC]

2.将 B.mBackStackNesting 赋值给 C,完成 BackStack 中对 B 的替换

3.B.mBackStackNesting减一 或者将  B.mBackStackNesting 置为 0, 使B在开启新界面时得到释放。


做完上述操作,才算真正的将 B destroy 掉而且保证堆栈的正确性。 除此,startFragmentAndDestroyCurrent 还提供了第二个参数, 这个参数是做什么的呢?是用来控制转场动画的。假设 A->B 使用 startFragment 和转场动画 a,B->C 使用 startFragmentAndDestroyCurrent和转场动画 b,那么从 C 返回到 A 时,该使用转场动画 a 还是 转场动画 b 呢?这就取决于你第二个参数传什么了。


内容就这么多了,就不贴源码了,是一堆的反射而已。如果有兴趣的话,就去 QMUI 看看吧。最近对文档的呼声越来越多了,哎,暂时被需求和 bug 压得死死的,有闲暇时间我们会尽量补上的,继续奋斗。

目录
相关文章
|
12月前
|
安全 API 数据安全/隐私保护
史上最全最完整,最详细,软件保护技术-程序脱壳篇-逆向工程学习记录(一)
欢迎访问我的原站!本文详细介绍了程序脱壳技术,包括壳的定义、作用、执行过程、OEP(原始入口点)的概念及查找方法。文章通过多个实例,逐步演示了如何使用OD(OllyDbg)等工具进行脱壳操作,涵盖了压缩壳、加密壳等多种类型的壳。内容详尽,适合逆向工程初学者深入学习。[点击查看原文](https://www.oisec.cn/index.php/archives/520/)
973 0
|
7月前
|
Prometheus Kubernetes 监控
OpenAI故障复盘丨如何保障大规模K8s集群稳定性
OpenAI故障复盘丨如何保障大规模K8s集群稳定性
213 0
OpenAI故障复盘丨如何保障大规模K8s集群稳定性
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
321 2
|
11月前
|
Unix Linux Shell
linux入门!
本文档介绍了Linux系统入门的基础知识,包括操作系统概述、CentOS系统的安装与远程连接、文件操作、目录结构、用户和用户组管理、权限管理、Shell基础、输入输出、压缩打包、文件传输、软件安装、文件查找、进程管理、定时任务和服务管理等内容。重点讲解了常见的命令和操作技巧,帮助初学者快速掌握Linux系统的基本使用方法。
629 3
|
11月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
1235 1
什么是AOP面向切面编程?怎么简单理解?
|
12月前
|
算法 Java 测试技术
🧑‍💻Java零基础:Java 的循环退出语句 break
【10月更文挑战第16天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
242 6
|
Android开发
DialogFragment 使用指南:几个小问题的解法
DialogFragment是Android中用于创建弹窗的特殊Fragment,继承自Fragment。使用步骤包括:1. 创建子类,2. 在onCreateView加载布局,3. onViewCreated初始化控件,4. 通过show方法显示。示例代码展示了一个基本的DialogFragment及其布局。此外,文中还解答了三个常见问题:如何设置弹窗宽度为match_parent,如何使弹窗位于屏幕底部,以及如何去除弹窗四周的默认padding。每个问题都提供了相应的解决方案,涉及在onStart中调整窗口参数和设置自定义样式。
1459 2
DialogFragment 使用指南:几个小问题的解法
|
开发工具 Android开发
Android studio历史版本下载
Android studio历史版本下载
4720 0
|
设计模式 缓存 Java
【Java|多线程与高并发】线程池详解
Java线程池是一种用于管理和重用线程的机制,它可以在需要执行任务时,从线程池中获取线程,执行任务,然后将线程放回池中,以便后续使用。线程池可以有效地管理线程的数量,提高程序的性能和资源利用率。
【Java|多线程与高并发】线程池详解
|
运维 监控 Kubernetes
读《SRE:Google运维解密》一点思考
## 0、为什么诞生SRE? + 原因一:企业成本的增长通用户的增长不成线性变化。但是随着系统的复杂度提升,组建越来越多,用户的流量压力也越来越大,相关的变更也会越来越多,各模块之间的变更顺序也会越来越复杂。
4764 0
读《SRE:Google运维解密》一点思考