【发布时间】:2020-10-14 02:50:10
【问题描述】:
简介
我正在使用 fragmentManager 的 API 来获取当前“backStack”的“快照”。此外,我正在使用 OnBackStackChangedListener 来执行一些依赖于这些“快照”的逻辑(在问题的末尾有更多关于这一点的信息)。
考虑当我们替换片段(C 为 D)时的情况:
[A,B,C] -> [A,B,D]
OnBackStackChangedListener 将触发一次并获取“快照”返回预期结果:
[A,B,D]
考虑另一种情况,即我们删除所有片段并添加另一个片段,即所谓的“newRoot”操作:
[A,B,C] -> [D]
Fragment 的 API 没有特殊的方法来做这种动作,所以我们首先使用 FragmentManager#popBackStack 然后 FragmentManager#add。在这种情况下 OnBackStackChangedListener 将触发两次。
问题是在第一次调用 OnBackStackChangedListener 时,“快照”有无效的(对我的逻辑无效) 状态:
[] instead of [D]
可能的解决方案 |问题本身
FragmentTransaction 有选项 setReorderingAllowed 可以做一些“优化魔法”并且 OnBackStackChangedListener 只触发一次。 setReorderingAllowed 的描述包含很多有用的信息,但没有关于 OnBackStackChangedListener 的内容,换句话说,setReorderingAllowed 和 OnBackStackChangedListener 之间没有记录的关系。
作为总结,工作(可能)方法:
1) executePendingTransactions
2) My actions
2.1) popBackStack
2.2) add (reorderingAllowed true, addToBackStack true)
// Expected: OnBackStackChangedListener called once
所以主要问题:
有人可以证明或揭开我的方法的神秘面纱,或者提出另一种方法来保证 OnBackStackChangedListener 在上述情况下的原子性吗?查源代码无果(过程太复杂)。
附:嵌套片段(childFragmentmanager、viewPager 等)超出了本题的范围
关于“一些逻辑”或我想要实现的目标
我正在尝试实现 DI 范围的自动管理(打开/关闭)。 (DI 又名依赖注入)
简而言之,Fragment 的 ViewModel(又名 VM)请求一些 DI 范围,例如类似“getOrOpen("scopeKey")”的东西。多个屏幕(片段)可以共享同一个范围,例如:
[A].getOrOpen("foo") // "foo" RefCount++ // 1
[B].getOrOpen("foo") // "foo" RefCount++ // 2
[C].getOrOpen("bar") // "bar" Refcount++ // 1
当片段被完全销毁(相应的事务从后台弹出)时,是时候尝试关闭范围(也就是 RefCount 中的递减),但是有一个问题,我们不能只递减计数器。考虑示例(A 替换 B):
[A] -> [B]
假设 A 和 B 需要相同的范围。 GC 清理我们的作用域可能存在间隙(因为 RefCount == 0),而且 Android 不保证调用 A 的 onDestroy 和 B 的 onCreate 的顺序。
所以我们应该在事务完全完成后进行清理。 OnBackStackChangedListener 做这件事的好地方。代替 create/destroy 中的 inc/dec refs,我们可以循环(foreach)backStack,计算 refs 并关闭未使用的范围。
附:在我的示例(架构)中,每个事务添加/替换单个片段,所以 backStack [A,B,C] 表示在 App 中打开了三个屏幕:A, Bstrong> 和 C。 (为简单起见,让我们忽略 DialogFragments)。还有事务的标签=片段的唯一ID,所以同一个片段的多个实例可以请求不同的范围。
【问题讨论】: