Fragment的原理和优化

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

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 的变更行为为: Fragment的原理和优化_第1张图片

现在,让我们来看看使用 addToBackStack(false) 时会发生什么: Fragment的原理和优化_第2张图片

回到之前提到的 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 呢?这就取决于你第二个参数传什么了。

 

 

 

Fragment运行机制源码分析(一)

Fragment运行机制源码分析(二) 

对Fragment的状态保存恢复机制原理的分析

对Fragment的BackStackRecord事务的分析

对Fragment、FragmentManager和BackStackRecord的字段全解析

你可能感兴趣的:(android源码分析)