共计 5088 个字符,预计需要花费 13 分钟才能阅读完成。
相比其它大多数 Android API,Fragments 近几年的变动要更大一些。最后它作为 Android 平台的一部分,起初成为 Android Support Library 的一部分,当初又以 AndroidX Fragments 的模式独立成为了 Jetpack 的一部分。
提醒 : 您不应该再须要应用 Android 框架里的 Fragment。除了它会在 Android 10 中被弃用以外,在弃用之前的这段漫长的工夫里尘封于框架中,不会有任何更新和破绽修复,同时也不会针对旧型号的设施或者旧版本的零碎进行兼容性适配 。
Android 架构组件 曾经接管了 Fragment 大量的传统职能 (比方应用 LifecycleObserver 来监听生命周期的回调或者应用 ViewModel 来放弃状态)。如果您应用 Fragment,就是通过 FragmentManager 来进行增加、移除和交互操作。
在 Fragment 1.3.0-alpha08 版本中 (最新版本 1.3.0-rc01),曾经实现了针对 FragmentManager 外部大量的重构工作。该版本通过更轻量的、测试性更强以及更加易于保护的外部类代替了大量原来在 FragmentManager 内实现的逻辑,该外部类的外围是 FragmentStateManager。
提醒 : 本文中我会议论很多对于 FragmentManager 的外部原理。简而言之: 请特地针对 Fragment 1.3.0-alpha08 进行回归测试,并且在发现任何回归景象后在 Issue Tracker 这里通知咱们。
新的状态管理器负责很多 Fragment 的关键环节:
- 在生命周期办法中挪动 Fragment
- 增加动画和切换成果
- 解决推延后的事务
咱们从底层剖析了本来零碎的实现机制,发现有一些 问题,所以重写了状态管理器。咱们解决了十几个已有的问题。通过重构,在独自的 FragmentManager 中反对多个返回堆栈了,并且还 简化了 Fragment 的生命周期。
FragmentManager 的 moveToState() 办法
每个 FragmentManager 都关联着一个宿主 (host)。在绝大多数 fragment 的用法中,FragmentActivity 是最突出的一个 (当然也有涵盖整个档次的 FragmentController 和 FragmentHostCallback 可用于构建自定义的宿主,这里咱们先不探讨它们)。随着 Activity 的生命周期在 CREATED、STARTED 和 RESUMED 状态中的转移,FragmentManager 也会相应的把这些状态的扭转传递到它的 Fragment 中。也就是 moveToState() 所施展的作用。
当然了,事件并没有这么简略间接。有很多条件逻辑能够管制 fragment 真正所处的状态,Activity 的生命周期状态 (或者对于嵌套的 Fragment 父级所处的状态) 仅仅是第一步,它能够作为 Fragment 所处状态的下限规范。这里的下限规范能够保障 Activity、Fragment 和它们的子级 Fragment 之间放弃正当的嵌套关系。
所以咱们在 简化 moveToState() 办法 的首要的工作就是将上述逻辑汇总到一个中央,所以就诞生了 FragmentStateManager。每个 Fragment 实例都在底层与一个 FragmentStateManager 相关联。通过在外部应用这个类,咱们能够从 FragmentManager 里去掉大量与 Fragment 交互的代码 ( 比方调用 Fragment 的 onCreateView 办法和其它与生命周期相干的办法)。
这样的代码拆散还能够让咱们通过一个独自的办法解决向前兼容所需的逻辑,即 Fragment 应该处于何种状态,而后将其汇总到一个中央: computeExpectedState()。该办法追踪所有以后的状态并且决定 Fragment 应该处于哪个状态。尽管 98% 的工夫里,Fragment 和它的宿主或父级 Fragment 所处的状态雷同,然而剩下的 2% 所产生的变动对基于 Fragment 的利用影响深远。
然而,有一种状况下咱们没有方法确定 Fragment 的理论状态: 提早加载的 Fragment。
提早加载的 Fragment
Fragment,无论好与坏,都从 Activity 上继承了大量雷同的命名规定和 API 调用接口。其中一部分继承是对于界面切换和在利用筹备好之前推延切换操作。该逻辑对于波及到共享元素切换的利用场景十分重要 (有时您心愿在场景切换之前就晓得将要加载的图片分辨率和在屏幕上的地位),同时也保障了在界面切换的过程中不会触发大量的加载操作。
提早加载的 Fragment 领有两大重要特质:
- 视图尽管被创立了,然而不可见;
- 生命周期的下限为 STARTED 状态。
当您调用 startPostponedEnterTransition() 的时候,fragment 的切换操作就开始了,视图会变为可见,Fragment 的状态会变为 RESUMED。而上述这些是由新的状态管理器实现的,之前的 Fragment 并不是这样的机制。作为参考,咱们这里援用一个相干的 问题形容:
当 Fragment 应用
postponeEnterTransition()
办法实现提早加载的时候,所冀望的成果是增加了 Fragment 的容器,在 Fragment 调用startPostponedEnterTransition()
之前,不运行任何进入界面的动画或者之前曾经在队列里的退出动画 (比方replace()
操作 )。另外一个预期的成果是当容器推延加载的时候,Fragment 不会进入 RESUMED 状态。然而,FragmentManager 仿佛并没有依照这个过程操作,而是将 Fragment 和整个 FragmentManager 置于一个奇怪的、不统一的状态。
换而言之,任何与以后被提早加载的 Fragment 相干的 FragmentTransaction 都会被回退到之前的状态 (比方返回到上一状态),然而这些 Fragment 并没有转换为适合的状态。
这样就导致了一系列问题:
- Fragment 的视图创立了,然而 Fragment 却没有被增加 (isAdded() 会返回 false)
- findFragmentById() 不会返回刚刚增加的 Fragment,即便调用 commitNow() 也不行
- 当 FragmentManager 启动后,Fragment 在一个中间状态卡住而不会追随启动 (https://issuetracker.google.com/issues/129035555)
- FragmentTransactions 的执行程序会被打乱 (https://issuetracker.google.com/issues/147297731)
- 容器的其它动画依然会播放 (比方曾经开始播放的弹出动画) (https://issuetracker.google.com/issues/37140383)
- onCreateView() 会被调用第二次 (https://issuetracker.google.com/issues/143915710)
事实上解决上述的任意问题都须要将整个提早加载 Fragment 所用到的回退处理过程替换掉,应用一套零碎放弃 FragmentManager 处于统一的、最新的状态,同时又能保留提早加载 Fragment 的一些重要的个性。
在容器层面进行操作
FragmentManager 蕴含一个好用的属性,您能够将 Fragment 所处容器的 ID 传递给该属性。甚至对于一个独自的 FragmentTransaction,您能够增加 Fragment 到容器,从另一个不同的容器中移除另外的 Fragment,替换第三个容器最上层的 Fragment 等等。操作的互相交织仅仅呈现在 Fragment 动画切入、切出的时候,这所有都只会在容器层面产生。
Fragment 反对多个动画零碎:
- 旧版的曾经 没什么理论作用 的 Animation API
- 开发框架里的 Animator API
- 开发框架里的 Transition API (仅仅反对 API 21 及以上,同样没什么作用了)
- AndroidX Transition API
您应该也晓得,命名是计算机科学中的一大难题,所以当咱们筹备构建一个类去管制所有这些 API 的时候,咱们费了一些功夫才决定将它命名为 SpecialEffectsController (该类不属于公共 API,所以将来还能够批改名称)。该类存在于容器层面,协调所有与 fragment 切入切出相干的 “ 特殊效果 ”。
SpecialEffectsController 是决定容器将来变动的惟一源头。换而言之,如果最先增加的 fragment 被延后加载了,整个容器都会被延后加载。再也不须要在 FragmentManager 层面实现其它逻辑,或者任何回退操作 (咱们提到过它能够影响多个容器)。因而,FragmentManager 处于正确的状态,并且咱们还可能取得所有提早加载的 Fragment 的非凡属性。
这个根底的 API 能够让咱们将所有酷炫成果的 API 集中到独自的 DefaultSpecialEffectsController 中,它负责实现切换成果和动画成果以及 Animator。也就是说将扩散在 FragmentManager 中的逻辑集中到一个中央。
“ 新的状态管理器 ” 意味着什么
其实它的意思是说将上面这个构造:
旧的状态管理器: 所有的逻辑都蕴含在 FragmentManager
替换为上面这样的构造:
新的状态管理器: FragmentManager 与独立的 FragmentStateManager 实例进行交互,而后 FragmentStateManager 再通过容器中的 SpecialEffectsController 协调其它 Fragment
通过拆散 FragmentManager,整体逻辑曾经在各个档次进行了大幅简化:
- FragmentManager 仅仅蕴含用于所有 fragment 的状态
- FragmentStateManager 在 fragment 层面治理状态
- SpecialEffectsController 在容器层面治理状态
职责拆散的设计构造使咱们扩大了 30% 的测试用例,笼罩了更多的利用场景,这些场景很多在互相孤立的状态下简直无奈测试。
会有行为变更须要解决吗?
不会。事实上,咱们在旧的和新的状态管理器之间运行了大量的 fragment 内部测试,以保障咱们实现足够数量的回归测试。
您能够在 版本公布日志 中找到和新的状态管理器相干的 bug 修复列表。所以能够看一下该列表,确保您的问题不是因为之前谬误的解决形式所造成的,同时也能够移除之前有问题的逻辑代码。
和 Fragment 1.2.0 中的 onDestroyView 的更新相相似,新的状态管理器会在您的 fragment 的切换 / 动画 /animator/ 特效完结之前始终保持在 STARTED 状态,而后无论是间接进行提早加载还是间接提早加载,所有的 fragment 状态都保持一致,这是因为它们属于雷同的容器。
如果产生行为变更,怎么办?
当您降级到 Fragment 1.3.0-alpha08 后,新的状态管理器是默认开启的。如果您发现了利用成果产生了变动,首先能够通过上面新增的实验性 API 测试该变动是否是因为新的状态管理器造成的:
FragmentManager.enableNewStateManager(false)
这个 API 是能够帮忙您禁用新的状态管理器,以帮忙您查看以后的变动是否和它相干。它帮忙您扫清了降级到 Fragment 1.3.0-alpha08 的阻碍,如果有任何问题,请在这个 Issue Tracker 中提交。
提醒 : FragmentManager.enableNewStateManager() API 是试验性质的。也就是说它并不蕴含在 Fragment 的稳固 API 中,并且可能在将来被移除。移除旧的代码是代码量升高的重要步骤,然而为了能让整个过程无误且顺利,咱们筹备在 Fragment 1.3.0 稳固版本公布之前都不会移除该 API。兴许能够思考在 Fragment 1.3.1 公布的时候移除该 API 的相干调用代码。
通过长达 11 个月的 100 多个独立批改,造就了 Fragment 最大的一次外部降级,并且为咱们带来了可维护性更高,可持续性更好以及更易了解的根底代码。这意味着 Fragment 的一致性更高,以及对您来说能够依赖更加巩固的根底代码来构建利用。咱们也十分欢送大家踊跃 提交问题 和反馈,一起参加到新的状态管理器的优化工作中来,使它变得更加欠缺。