【问题标题】:"Atomicity" for OnBackStackChangedListener (deep internals of fragment's core)OnBackStackChangedListener 的“原子性”(片段核心的深层内部结构)
【发布时间】: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 的内容,换句话说,setReorderingAllowedOnBackStackChangedListener 之间没有记录的关系。

作为总结,工作(可能)方法:

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]

假设 AB 需要相同的范围。 GC 清理我们的作用域可能存在间隙(因为 RefCount == 0),而且 Android 不保证调用 A 的 onDestroy 和 B 的 onCreate 的顺序。

所以我们应该在事务完全完成后进行清理。 OnBackStackChangedListener 做这件事的好地方。代替 create/destroy 中的 inc/dec refs,我们可以循环(foreach)backStack,计算 refs 并关闭未使用的范围。

附:在我的示例(架构)中,每个事务添加/替换单个片段,所以 backStack [A,B,C] 表示在 App 中打开了三个屏幕:A, B​​strong> 和 C。 (为简单起见,让我们忽略 DialogFragments)。还有事务的标签=片段的唯一ID,所以同一个片段的多个实例可以请求不同的范围。

【问题讨论】:

    标签: android android-fragments


    【解决方案1】:

    查看FragmentManager source code,只有reportBackStackChanged()(调用所有OnBackStackChangedListener实例的方法)被调用的地方——在executeOpsTogether()方法的末尾。 executeOpsTogether() 的职责是将一个或多个 FragmentTransactions(注意内部的 BackStackRecord 类实现了 FragmentTransaction)作为单个原子操作。

    该方法只能从一种方法调用,removeRedundantOperationsAndExecute()。此方法查看每个 FragmentTransaction 并检查是否已调用 setReorderingAllowed(true)mReorderingAllowed 标志)。

    如果所有操作都设置了该标志,则此方法只调用一次executeOpsTogether()。这就是为什么您只看到一个对您的OnBackStackChangedListener 的回调,而这正是the Navigation Component always uses setReorderingAllowed(true) 的原因。

    因此,只要您使用 setReorderingAllowed(true)commit()popBackStack()(即,不是 commitNow()popBackStackImmediate(),它们都只立即执行一个操作),所有排队的操作都将作为单个操作执行原子操作并导致对 OnBackStackChangedListener 的单个回调,每个回调中的状态一致 - 当调用该侦听器时,不会有其他待处理的操作。

    【讨论】:

      猜你喜欢
      • 2018-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-03
      • 2023-03-23
      • 1970-01-01
      相关资源
      最近更新 更多