官宣|全新的 Fragment: 请使用新的状态管理器!
The following article is from 谷歌开发者 Author Android
AndroidX Fragments https://developer.android.google.cn/jetpack/androidx/releases/fragment
Android 架构组件 https://developer.android.google.cn/topic/libraries/architecture
Fragment 1.3.0-alpha08 https://developer.android.google.cn/jetpack/androidx/releases/fragment#1.3.0-alpha08 FragmentStateManager https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java?ss=androidx
Issue Tracker https://issuetracker.google.com/issues/new?component=460964
新的状态管理器负责很多 Fragment 的关键环节:
在生命周期方法中移动 Fragment
添加动画和切换效果
处理推迟后的事务
查看在 Issue Tracker 上的讨论 https://issuetracker.google.com/issues/80029773 阅读相关文章 Fragment 的过去、现在和将来
FragmentManager 的 moveToState() 方法
每个 FragmentManager 都关联着一个宿主 (host)。在绝大多数 fragment 的用法中,FragmentActivity 是最突出的一个 (当然也有涵盖整个层次的 FragmentController 和 FragmentHostCallback 可用于构建自定义的宿主,这里我们先不讨论它们)。随着 Activity 的生命周期在 CREATED、STARTED 和 RESUMED 状态中的转移,FragmentManager 也会相应的把这些状态的改变传递到它的 Fragment 中。也就是 moveToState() 所发挥的作用。
当然了,事情并没有这么简单直接。有很多条件逻辑可以控制 fragment 真正所处的状态,Activity 的生命周期状态 (或者对于嵌套的 Fragment 父级所处的状态) 仅仅是第一步,它可以作为 Fragment 所处状态的上限标准。这里的上限标准可以保证 Activity、Fragment 和它们的子级 Fragment 之间保持合理的嵌套关系。
简化 moveToState() 方法 https://issuetracker.google.com/139536619 FragmentStateManager https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java?ss=androidx
computeExpectedState() https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java;l=165?ss=androidx
然而,有一种情况下我们没有办法确定 Fragment 的实际状态: 延迟加载的 Fragment。
延迟加载的 Fragment
Fragment,无论好与坏,都从 Activity 上继承了大量相同的命名规则和 API 调用接口。其中一部分继承是关于界面切换和在应用准备好之前推迟切换操作。该逻辑对于涉及到共享元素切换的应用场景非常重要 (有时您希望在场景切换之前就知道将要加载的图片分辨率和在屏幕上的位置),同时也保障了在界面切换的过程中不会触发大量的加载操作。
视图虽然被创建了,但是不可见; 生命周期的上限为 STARTED 状态。
当您调用 startPostponedEnterTransition() 的时候,fragment 的切换操作就开始了,视图会变为可见,Fragment 的状态会变为 RESUMED。而上述这些是由新的状态管理器实现的,之前的 Fragment 并不是这样的机制。作为参考,我们这里引用一个相关的问题描述:
延迟加载的 Fragments 使得 Fragments 和 FragmentManager 处于一个不一致的状态 https://issuetracker.google.com/147749580
当 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 动画切入、切出的时候,这一切都只会在容器层面发生。
旧版的已经没什么实际作用的 Animation API 开发框架里的 Animator API 开发框架里的 Transition API (仅仅支持 API 21 及以上,同样没什么作用了) AndroidX Transition API
没什么实际作用 https://issuetracker.google.com/163084315#comment4 AndroidX Transition API https://developer.android.google.cn/jetpack/androidx/releases/transition
SpecialEffectsController https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.java
DefaultSpecialEffectsController https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
"新的状态管理器" 意味着什么
其实它的意思是说将下面这个结构:
旧的状态管理器: 所有的逻辑都包含在 FragmentManager
替换为下面这样的结构:
FragmentManager 仅仅包含用于所有 fragment 的状态 FragmentStateManager 在 fragment 层面管理状态 SpecialEffectsController 在容器层面管理状态
职责分离的设计结构使我们扩展了 30% 的测试用例,覆盖了更多的应用场景,这些场景很多在相互孤立的状态下几乎无法测试。
会有行为变更需要处理吗?
不会。事实上,我们在旧的和新的状态管理器之间运行了大量的 fragment 内部测试,以保证我们完成足够数量的回归测试。
您可以在版本发布日志中找到和新的状态管理器相关的 bug 修复列表。所以可以看一下该列表,确保您的问题不是由于之前错误的处理方式所造成的,同时也可以移除之前有问题的逻辑代码。
和 Fragment 1.2.0 中的 onDestroyView 的更新相类似,新的状态管理器会在您的 fragment 的切换/动画/animator/特效结束之前始终保持在 STARTED 状态,然后无论是直接进行延迟加载还是间接延迟加载,所有的 fragment 状态都保持一致,这是因为它们属于相同的容器。
版本发布日志 https://developer.android.google.cn/jetpack/androidx/releases/fragment#1.3.0-alpha08
如果发生行为变更,怎么办?
FragmentManager.enableNewStateManager(false)
Fragment 1.3.0-alpha08
https://developer.android.google.cn/jetpack/androidx/releases/fragment#1.3.0-alpha08
这个 API 是可以帮助您禁用新的状态管理器,以帮助您检查当前的变化是否和它相关。它帮助您扫清了升级到 Fragment 1.3.0-alpha08 的障碍,如果有任何问题,请在这个 Issue Tracker 中提交。
提交 Fragment 相关问题 https://issuetracker.google.com/issues/new?component=460964
提示: FragmentManager.enableNewStateManager() API 是实验性质的。也就是说它并不包含在 Fragment 的稳定 API 中,并且可能在未来被移除。移除旧的代码是代码量降低的重要步骤,但是为了能让整个过程无误且顺利,我们准备在 Fragment 1.3.0 稳定版本发布之前都不会移除该 API。也许可以考虑在 Fragment 1.3.1 发布的时候移除该 API 的相关调用代码。
通过长达 11 个月的 100 多个独立修改,造就了 Fragment 最大的一次内部升级,并且为我们带来了可维护性更高,可持续性更好以及更易理解的基础代码。这意味着 Fragment 的一致性更高,以及对您来说可以依赖更加稳固的基础代码来构建应用。我们也非常欢迎大家积极提交问题和反馈,一起参与到新的状态管理器的优化工作中来,使它变得更加完善。
提交问题 https://issuetracker.google.com/issues/new?component=460964
---END---
永久封禁!印度政府再作妖,涉及微信、TikTok等59款App
更文不易,点个“在看”支持一下👇