页面路由的实现与差异
在前面的页面分析过程中,多次提到了channel通道的使用。这块通道逻辑都在container_coordinator.dart中,coordinator有调度员
的意思,这大概就是路由管理封装类命名的原因吧。
路由绑定
既然是容器调度,那么调度员第一步要做的是什么呢? 自然是先要有可以被灵活调度的容器。因此,Boost使用的第一步就是先进行路由的注册配置。当我们执行registerPageBuilders时,它的具体实现如下:
/// flutter_boost.dart
///Register a map builders
void registerPageBuilders(Map<String, PageBuilder> builders) {
ContainerCoordinator.singleton.registerPageBuilders(builders);
}
可以看到与原生route非常类似,我们仅仅是传入了一个键值对的Map。其中Key是页面路由的么名字,Value其实是一个Builder,类似于WidgetBuilder。差异在于Boost额外封装了一些容器维度的属性,比如说页面的唯一标识,参数等。所以PageBuilder可以简单理解为就是一个个性化的专用WidgetBuilder。
页面配置
有了路由表以后,比那可以在合适的时候调用,从路由表中获取目标页面的PageBuilder,然后构建出一个真正的Widget页面。这一步又是怎么实现的呢?
在《顶层容器栈的实现》一节中,我们通过检索路由表被调用的逻辑,知道当Dart侧收到页面创建事件willShowPageContainer时,回去实例化一个页面。实例化页面的时候,根据事件的参数,构造了一个页面维度的配置,叫做BoostContainerSettings
,配置中的WidgetBuilder是用和原生路由配合工作的。
class BoostContainerSettings {
final String uniqueId;
final String name;
final Map params;
final WidgetBuilder builder;
const BoostContainerSettings(
{this.uniqueId = 'default',
this.name = 'default',
this.params,
this.builder});
}
配置的生成逻辑如下,当WidgetBuilder被Flutter触发的时候,将构建Widget的任务委托给了PageBuilder处理:
/// conatiner_coordinator.dart
BoostContainerSettings _createContainerSettings(
String name, Map params, String pageId) {
Widget page;
final BoostContainerSettings routeSettings = BoostContainerSettings(
uniqueId: pageId,
name: name,
params: params,
builder: (BuildContext ctx) {
//Try to build a page using keyed builder.
if (_pageBuilders[name] != null) {
page = _pageBuilders[name](name, params, pageId);
}
//Build a page using default builder.
if (page == null && _defaultPageBuilder != null) {
page = _defaultPageBuilder(name, params, pageId);
}
assert(page != null);
Logger.log('build widget:$page for page:$name($pageId)');
return page;
});
return routeSettings;
}
进一步的,我们继续分析下,这个WidgetBuilder什么时候会被Flutter触发呢?
简单来说是在Navigator中触发的。我们已经知道BoostContainer本身继承了Navigator,所以它是被当做Navigator来使用的。在构建BoostContainer时,构造器如下:
factory BoostContainer.obtain(
Navigator navigator, BoostContainerSettings settings) =>
BoostContainer(
key: GlobalKey<BoostContainerState>(),
settings: settings,
onGenerateRoute: (RouteSettings routeSettings) {
if (routeSettings.name == '/') {
return BoostPageRoute<dynamic>(
pageName: settings.name,
params: settings.params,
uniqueId: settings.uniqueId,
animated: false,
settings: routeSettings,
builder: settings.builder);
} else {
return navigator.onGenerateRoute(routeSettings);
}
},
observers: <NavigatorObserver>[
ContainerNavigatorObserver.bindContainerManager()
],
onUnknownRoute: navigator.onUnknownRoute);
通过onGenerateRoute,将Boost的个性化页面创建最终与Navigator关联了起来。
Boost事件定义
事件的定义目前内置了4种,分别是:
- backPressedCallback 返回键事件
- foreground 页面进入前台
- background 页面进入后台
- scheduleFrame 调度一次页面绘制
前后台事件触发后,会对当前状态进行标记,具体是通过Boost单例,获取的当前的目标Container容器;最后通知观察者。 返回事件的处理则会分发至当前Container,触发一次页面的pop动作。
Boost方法定义
在使用Boost过程中,我们可以观测到很多标签日志输出,如果要查方法相关的日志,可以过滤关键词onMetohdCall。 目前支持的核心方法包括:
- didInitPageContainer
- willShowPageContainer
- didShowPageContainer
- willDisappearPageContainer
- didDisappearPageContainer
- willDeallocPageContainer
- onNativePageResult
几乎都是围绕着两个页面切换过程中的状态进行的定义。有一个需要注意就是针对页面show的两个事件处理:
- willShowPageContainer
- didShowPageContainer
willShow在源码中不会通知其自定义的生命周期回调,didShow会通知。除此之外,两者的实现中都有对页码创建。为什么会存在这种逻辑,目前单纯分析Dart侧代码得不到解释,后续在分析Native源码时可以进一步确认相关调用链路。