【Fragment多返回栈】开篇,官方为什么要支持 Fragment 多返回栈,Navigation 所谓的重建问题是什么?
前言
很高兴见到你 👋
「Fragment多返回栈」是一个新的系列,主要追踪官方对 Fragment 多返回栈这一功能的实现过程,透过该过程,我们可以学习官方面对一个新功能是如何进行开发的。
本文是该系列的开篇,内容分为三部分:
简单介绍 Fragment 返回栈,并说明单返回栈与多返回栈的实现方式 简单解释 Navigation 所谓的重建 bug 简单梳理官方对此多返回栈功能支持的规划
本文对上述内容均是简单介绍,使用图片的方式形象地表述该部分内容(为此专门学习了 Sketch 🤣,我这么拼你好意思不点赞嘛?😋)。
关于源码详解,以及官方对此多返回栈功能支持全部时间线,可参考我之前的文章 【背上Jetpack之Navigation】想去哪就去哪,Android世界的指南针。
OK,让我们开始吧~
Fragment 还有返回栈?
提到返回栈,大多数小伙伴应该想到 Activity 返回栈。其实 Fragment 也是有返回栈的。关于 Fragment 的源码解析可以参考我之前写的 从源码的角度看Fragment 返回栈 附多返回栈demo。
简单总结一下 Fragment 的返回栈:
使用 addToBackStack() 方法可以将 Fragment 添加到返回栈。其内部将 mAddToBackStack
赋值为 true。
//FragmentTransaction.java
public FragmentTransaction addToBackStack(@Nullable String name) {
mAddToBackStack = true;
mName = name;
return this;
}
FragmentManager
的实现类 FragmentManagerImpl
内部通过 ArrayList
管理返回栈。
如果 mAddToBackStack
为 true,则将 Fragment 加入到返回栈。
//BackStackRecord.java
public boolean generateOps(ArrayList records, ArrayList isRecordPop) {
//...
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
return true;
//FragmentManagerImpl.java
void addBackStackState(BackStackRecord state) {
mBackStack.add(state);
}
Fragment 单返回栈
「现有 API,一个 FragmentMnager 只对一个应返回栈」。我们举一个形象的例子
如下图所示,Activity 中使用四个 FragmentContainerView
作为不同花色扑克的容器。
其中每个容器内会按牌面大小顺序发牌,如 ♥A → ♥2 → ♥3... 最终收牌时会严格按照发牌顺序进行回收。效果如下所示:
上述示例使用 Activity 的 FragmentManager 管理所有 Fragment,它们被组织在一个线性的结构里。我们可以将其理解为 「单返回栈」。
实现多返回栈
前文我们提到,现有 API 一个 FragmentMnager 只对应一个返回栈,官方还没有提供单 FragmentMnager 管理多个返回栈的 API。那么我们可以实现多返回栈吗?答案是可以的,我们可以使用多个 FragmentManager 来实现。
如上图所示,绿色部分为 Fragment 容器,顶部四个 tab 分别对应不同花色。创建四个 NavHostFragment
(自定义的,与 navigation 无关)。点击其中一个花色 tab 便将对应的 NavHostFragment
attach 到容器,并将其它 NavHostFragment
dettach。最终效果如下:
这里的关键是使用了四个 NavHostFragment
(上图中四个花色的 NavHostFragment 实例不同)作为每个返回栈的「宿主」,它们拥有自己的 FragmentManager
,维护着自己的返回栈。
Navigation 所谓的 bug 究竟是怎么回事?
「Navigation 在管理带有平级关系的界面时会出现问题」,【背上Jetpack之Navigation】想去哪就去哪,Android世界的指南针 一文中已经探讨过该问题。
这里举例说明一下:
Navigation 重建问题
新建 Project,选择 Bottom Navigation Activity 模版:
该项目由一个 Activity(MainActivity) 以及三个 Fragment(Home,Dashboard,Notifications)组成。
结构示意图如下:
在内部,三个 Fragment(Home,Dashboard,Notifications)都由 androidx.navigation.fragment.NavHostFragment
管理,即使用 NavHostFragment
的 FragmentManager
管理返回栈。
每次点击底部按钮,都会新建 Fragment 实例(但它们的宿主未发生变化):
这里出现了不符合预期的现象,如果从 HomeFragment 点击跳转到 ChildFragment,点击 Dashboard 按钮后再点击 Home 按钮,由于 HomeFragment 重建,原来的 ChildFragment 消失。如下图:
该问题的本质
本质上,上面的示例是一个单返回栈的模型。因为三个 Fragment(Home,Dashboard,Notifications)都由 androidx.navigation.fragment.NavHostFragment
直接管理,而目前单个 FragmentManager 仅支持一个返回栈。
而这种平级的模型是需要多返回栈进行管理的,即 Home,Dashboard,Notifications 均拥有各自的返回栈。它们相互独立,互补影响。但以现有的 API 实现起来比较复杂,实现方式参考多返回栈一节。
官方出手了
上述问题早在 2018 年就已提出,地址 在这。
而在 2021 年 1 月,官方终于开始着手解决这一问题。
该功能将在 Fragment 1.4.0 和 Navigation 2.4.0 版本提供。
总结
Navigation 管理带有平级关系的界面时,以现有的 API 会产生不符合预期的现象。本质上,该问题是由于现有的 API 不支持多返回栈造成的。好在官方已开始着手开发这一功能,该功能将在 Fragment 1.4.0 和 Navigation 2.4.0 版本提供。发布正式版可能需要一年多的时间。虽然很慢,好在有希望。该系列会持续追踪这一过程,让我们看看官方是如何在已有项目上进行新功能开发的吧~😉
我们下期见。
关于我
人总是喜欢做能够获得正反馈(成就感)的事情,如果感觉本文内容对你有帮助的话,麻烦点亮一下👍,这对我很重要哦~
我是 Flywith24,「人只有通过和别人的讨论,才能知道我们自己的经验是否是真实的」,加我微信交流,让我们共同进步。