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