1.5.3 第二代混合技术方案 FlutterBoost
1.重构计划
闲鱼在推进Flutter 化过程当中,遇到了更加复杂的页面场景,也逐渐暴露了老方案的局限性和一些问题。所以,闲鱼启动了代号为FlutterBoost的新混合技术方案。我们的主要目标有:
- 可复用通用型混合方案。
- 支持更加复杂的混合模式,例如支持主页Tab。
- 无侵入性方案,不再依赖修改Flutter 的方案。
- 支持通用页面生命周期。
- 统一明确的设计概念。
跟老方案类似,新的方案仍采用共享引擎的模式实现。主要思路是由Native 容器通过消息驱动Flutter 页面容器,从而达到Native 容器与Flutter容器的同步目的。希望做到Flutter 渲染的内容是由Naitve 容器来驱动的。
简单地理解,闲鱼想把Flutter 容器做得像浏览器一样。填写一个页面地址,然后由容器管理页面的绘制。在Native 侧,开发者只需要关心如何初始化容器,然后设置容器对应的页面标志即可。
2.主要概念(如图1-21)
图1-21
(1)Native 层
- Container:Native 容器、平台Controller、Activity 和ViewController。
- Container Manager:容器的管理者。
- Adaptor:Flutter 是适配层。
- Messaging:基于Channel 的消息通信。
(2)Dart 层
- Container:Flutter 用来容纳Widget 的容器,具体实现为Navigator 的派生类。
- Container Manager:Flutter 容器的管理者,提供show、remove 等API。
- Coordinator:协调器,接受Messaging 消息,负责调用Container Manager的状态管理。
- Messaging:基于Channel 的消息通信。
(3)关于页面的理解
在Native 和Flutter 中,表示页面的对象和概念是不一致的。在Native中,对于页面的概念一般是ViewController 和Activity。而在Flutter 中,对于页面的概念是Widget。我们希望可统一页面的概念,或者说弱化Flutter本身的Widget 对应的页面概念。换句话说,当一个Native 的页面容器存在的时候,FlutteBoost 保证一定会有一个Widget 作为容器的内容。所以,我们在理解和进行路由操作的时候,都应该以Native 的容器为准,Flutter Widget 依赖于Native 页面容器的状态。
在FlutterBoost 的概念里说到页面的时候,指的是Native 容器和它所附属的Widget。所有页面的路由操作,打开或者关闭页面,实际上都是对Native 页面容器的直接操作。无论路由请求来自何方,最终都会转发给Native 实现路由操作。这也是接入FlutterBoost 时需要实现Platform 协议的原因。
另一方面,我们无法控制业务代码通过Flutter 本身的Navigator 去push新的Widget。对于业务不通过FlutterBoost 而直接使用Navigator 操作Widget 的情况,建议由业务自己负责管理其状态。这种类型的Widget 不属于FlutterBoost 所定义的页面概念。
理解这里的页面概念,对于理解和使用FlutterBoost 至关重要。
3.与老方案的主要差别
老方案在Dart 层维护单个Navigator 栈结构用于Widget 的切换。而新的方案则是在Dart 侧引入了Container 的概念,不再用栈的结构维护现有的页面,而是通过将Key-Value 映射扁平化的形式去维护当前所有的页面,每个页面拥有一个唯一的ID 地址。这种结构很自然地支持了页面的查找和切换,不再受制于栈顶操作,一些由于Pop 导致的问题迎刃而解。同时,也不再依赖修改Flutter 源码的形式去实现,避免了实现的侵入性。
这是如何做到的呢?
Flutter 在底层提供了让开发者自定义Navigator 的接口,闲鱼自己实现了一个管理多个Navigator 的对象。当前最多只会有一个可见的Flutter Navigator,它包含的页面也就是当前可见容器所对应的页面。
Native 容器与Flutter 容器(Navigator)是一一对应的,生命周期也是同步的。当一个Native 容器被创建的时候,Flutter 的一个容器也被创建,它们通过相同的ID 地址关联起来。当Native 的容器被销毁的时候,Flutter的容器也被销毁。Flutter 容器的状态跟随Native 容器而变化,这也就是Native 驱动。由Manager 统一管理切换当前在屏幕上展示的容器。
下面用一个简单的例子描述一个新页面创建的过程:
- 创建Native 容器(iOS ViewController,Android Activity or Fragment)。
- Native 容器通过消息机制通知Flutter Coordinator 新的容器被创建。
- Flutter Container Manager 得到通知,负责创建出对应的Flutter 容器,并且在其中装载对应的Widget 页面。
- 当Native 容器展示到屏幕上时,容器给Flutter Coordinator 发消息,通知要展示页面的ID 地址。
- Flutter Container Manager 找到对应ID 地址的Flutter Container 并将其设置为前台可见容器。
这就是一个新页面创建的主要逻辑,销毁和进入后台等操作也类似由Native 容器事件去驱动。
目前,FlutterBoost 已经在生产环境支撑闲鱼客户端中所有的基于Flutter 开发的业务,为更加负复杂的混合场景提供了支持,同时也解决了一些历史遗留问题。闲鱼在项目启动之初就希望FlutterBoost 能够解决Native App 混合模式接入Flutter 这个通用问题。所以把它做成了一个可复用的Flutter 插件,希望吸引更多感兴趣的朋友参与到Flutter 社区的建设中来。闲鱼的方案可能不是最好的,希望看到社区能够涌现出更加优秀的组件和方案。
1.5.4 扩展补充
1.性能相关
在对两个Flutter 页面进行切换时,因为只有一个Flutter View,所以需要对上一个页面进行截图保存。如果Flutter 页面较多,则截图会占用大量内存。这里采用文件内存二级缓存策略,在内存中最多只保存2~3 个截图,其余的截图在写入文件时按需加载。这样一来,可以在保证用户体验的同时,使内存也保持在一个较为稳定的水平。
在页面渲染性能方面,Flutter 的AOT 优势展露无遗。当页面快速切换的时候,Flutter 能够很灵敏地进行相应页面的切换,在逻辑上创造出一种Flutter 有多个页面的感觉。
2.Release 1.0 支持
在项目开始的时候,闲鱼基于目前使用的Flutter 版本进行开发,而后进行了Release 1.0 兼容升级测试且没有发现问题。
3.接入
只要是集成了Flutter 的项目,都可以用官方依赖的方式,非常方便地以插件形式引入FlutterBoost,只需要对工程进行少量代码接入即可。详细接入文档,请参阅GitHub 主页官方项目文档。