理解 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 压得死死的,有闲暇时间我们会尽量补上的,继续奋斗。

目录
相关文章
|
敏捷开发 存储 搜索推荐
《阿里巴巴Java开发手册v1.4.0(详尽版)》更新,新增16条设计规约
阿里巴巴集团推出的《阿里巴巴Java开发手册》是阿里巴巴近万名开发同学集体智慧的结晶,以开发视角为中心,详细列举如何开发更加高效、更加容错、更加有协作性,力求知其然,更知其不然,结合正反例,让Java开发者能够提升协作效率、提高代码质量。
732334 3
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
191918 61
|
3月前
|
存储 弹性计算 网络安全
对象存储OSS产品常见问题之图片无法在网页显示如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。对象存储OSS产品常见问题之
785 0
|
3月前
|
Shell Android开发
安卓逆向 -- 防抓包破解(JustTrustMe)
安卓逆向 -- 防抓包破解(JustTrustMe)
165 1
|
3月前
|
传感器 人工智能 网络协议
MQTT.fx的安装和使用
MQTT.fx的安装和使用
558 0
|
存储 数据采集 运维
日志服务(SLS)使用体验
通过参加配置SLS来实现Nginx日志的采集的实验,以及参与了数据洞察创新挑战赛之智能运维赛,来谈谈自己的体验感受。
1390 55
|
存储 缓存 JavaScript
Vue Elementui+SpringBoot做大文件切片上传
Vue Elementui+SpringBoot做大文件切片上传
4355 0
Vue Elementui+SpringBoot做大文件切片上传
|
XML 缓存 Android开发
QMUI实战(二)—Activity 和 Fragment,我们该选择谁?
在一开始,官方只提供了 Activity 来作为 UI 界面的载体,因此我们也别无选择,只能用它。而在 Android 3.0 后,Fragment 也面世了,它一开始是用于适配平板的,以邮件列表与详情的适配为例,手机端够小,因此开始展示列表,点击进入详情,而平板够大,则可以列表显示在左侧,详情显示在右侧,点击列表只是切换详情。对于这种适配场景,列表页和详情页必须在同一个 Activity 里了,而这便是我所知道的 Fragment 诞生的场景了。
165 0
|
测试技术 Android开发 Kotlin
QMUI实战(一)—为何我们要使用 LauncherActivity?
QMUI 2 发布了,但是里面换肤等相关的很多东西,如果不讲,那么很多人估计就只能复制粘贴下 QMUIDemo 的代码,而并不能用好 QMUI, 或者是通过 QMUI 来提升自己的 UI 开发能力,毕竟现在很多 Android 开发都是轻 UI 而重数据流,遇到需要复杂 UI 的地方,在 Github 上随便找个组件套上去就行了,如果找得到刚好符合需求的还好,如果找到的不是那么切合需求,那一天的状态很可能就是“一壶茶、一包烟、几个间距颜色调一天”了。
194 0