简单来说,Flutter-Boost就是开发过程中Flutter与原生的桥梁,想要明白Flutter-Boost做了什么,首先需要简单了解Flutter的基本知识。
Flutter 基本原理
Flutter架构
Framework:业务接触到的主要API,应用是基于Framework层开发的
Engine:用C、C ++、Dart和Skia(2D渲染引擎)构建。负责线程管理、Dart VM状态管理和Dart代码加载等工作。
Embedder:除此之外,flutter在使用的过程中还有Embedder一层,主要用来,把Flutter嵌入到各个平台上去。具体来说做了:渲染Surface设置,线程设置,以及插件管理。具体通过四个Task Runner实现的(Platform Task Runner、UI Task Runner、GPU Task Runner、IO Task Runner)
Flutter的特点
相比于其他的动态化框架,如H5、Weex,Flutter,有以下的区别:
- 运行原理 React-Native、Weex核心是通过Javascript开发,执行时需要Javascript解释器。而Flutter不涉及JavaScript解释器,Flutter的任何Dart代码都是AOT编译成本地代码的
- 实际体验 Flutter自带渲染引擎, 使用 Dart 构建的抽象的视图结构数据加工成 GPU 数据,交由 OpenGL 最终提供给 GPU 渲染,可以在最大程度上保证一款应用在不同平台、不同设备上的体验一致性。这一点是其他动态化框架做不到的。 具体可以参考链接:跨平台移动开发技术调研
Engine 与 Isolate
Engine
像上文中所讲到的那样,Engine主要包括
- Skia 是一个 2D 的图形渲染库,可在多种软硬件平台上运行。
- Dart,一个具有垃圾回收和面向对象语言的虚拟机。 其中重点在第二个部分:Engine不会自己创建或者管理自身的"线程"。相反,Embedder(fluter的运行平台)负责给Engine创建和管理“线程”(以及消息循环)。Embedder将自己管理的“线程”作为 task runner 提供给Flutter 引擎。除了Embedder环境管理的线程,Dart 虚拟机也有自己的线程池。不论是 Engine还是Embedder都无法访问 Dart 虚拟机线程池中的线程。
Isolate
Dart是单线程的,想要实现多线程的方式,只有通过isolate。Isolate的本质是类似于线程,每个isolate都是隔离的,并不会共享内存,都有独自的运行时的栈和堆。不同的isolate之间不能直接共享任何的状态,只能通过消息端口来进行通信,这是和线程的概念最不同的地方。
Flutter-Boost
研究一个新的技术,总是聚焦于:他是干啥的,怎么干的,有什么优缺点?现在我们就聚焦于第一个问题:他是干啥的? Flutter-Boost是用来解决Native 页面与 Flutter 页面共存以及通信问题的。然而现如今官方的Flutter已经支持在原生的项目中嵌入Flutter,为何还要大费周章自己搞呢?想明白这个问题,需要先看一下google官方给提供的开发方案:
Google官方的混合开发
• 现如今Flutter官方在AS中已经提供了将flutter作为模块,导入的原生的工程当中,实现了三端分离的开发模式(三端分离指:将Flutter工程作为原生工程的子模块,维持原有的原生工程管理方式不变)。截止到2020.12.25,Flutter Module已经支持如下的特性:
可以看到,flutter model只是简单的实现了三段分离模式的设计,但在性能上存在问题。
• Google官方所使用的是多引擎模式,也就是说对于间隔出现的Flutter页面,每次都需要初始化一个新的引擎,举个例子:打开一下一组页面:
Flutter Page1 -> Flutter Page2 -> Native Page1 -> Flutter Page3
需要在在Flutter Page1和Flutter Page3创建两个引擎,如果情况再极限一些
Flutter Page1 -> Native Page1->Flutter Page2 -> Native Page2 -> Flutter Page3
每次打开一个Flutter页面都创建了一个引擎,然而Flutter Engine本身是一个比较重的对象,显然不适合真正的使用。
• 此外Google官方的混合开发还存在以下的问题:
a. - 日志不能输出到原生端;
b. - 存在内存泄漏的问题,使用boost可以让内存稳定;
c. - 页面生命周期的管理不够整齐
Flutter-Boost的混合开发简介
为了,解决上述的问题,Flutter-Boost采用了共享引擎的方式,主要的思路为:
由Native容器Container通过消息驱动Flutter页面容器Container,,从而达到Native Container与Flutter Container的同步目的。其中Flutter渲染的内容是由Naitve容器去驱动的。咸鱼官方将Flutter容器比作浏览器,填写一个页面地址,然后由容器去管理页面的绘制,原生界面只需要初始化Flutter的容器,设置容器的页面标志即可。
各个部分的作用依次是
Native层
- Container:Native容器,对应代码BoostFlutterActivity
- Container Manager:容器的管理者,对应代码FlutterViewContainerManager
- Messaging:基于Channel的消息通信,对应代码FlutterBoostPlugin Dart层:dart代码不在项目之中,具体存储在flutter的sdk缓存里,double shift可以找到
- Container:Flutter用来容纳Widget的容器,对应代码BoostContainer
- Container Manager:Flutter容器的管理,提供show,remove等Api,对应代码BoostContainerManager
- Coordinator: 协调器,接受Messaging消息,负责调用Container Manager的状态管理。对应代码ContainerCoordinator
- Messaging:基于Channel的消息通信,对应代码BoostChannel
如何实现
页面跳转
- 创建Native容器,也就是Activity和fragment,
- Activity或fragment通过消息机制(Messaging)通知Flutter Coordinator新的Native容器已经被创建
- Flutter Container Manager得到通知,负责创建相应的Flutter容器,并在其中装载对应的widget页面
- 当Native容器(是Activity和fragment)展示到屏幕上时,给Flutter Coordinator发消息,通知展示页面的ID地址
- Flutter Container Manager找到对应的ID地址的Flutter Container,并将其设置为前台可见容器。
通信
- Flutter 向 原生传递消息:通过MethodChanel实现,代码如下: 原生部分:
@Override
public void onEngineCreated() {
// 注册MethodChannel,监听flutter侧的getPlatformVersion调用
MethodChannel methodChannel = new MethodChannel(FlutterBoost.instance().engineProvider().getDartExecutor(), "flutter_native_channel");
methodChannel.setMethodCallHandler((call, result) -> {
if (call.method.equals("getPlatformVersion")) {
result.success(Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
});
}
flutter部分:
// flutter 侧MethodChannel配置,channel name需要和native侧一致
static const MethodChannel _methodChannel =
MethodChannel('flutter_native_channel');
String _systemVersion = '';
Future<dynamic> _getPlatformVersion() async {
try {
final String result =
await _methodChannel.invokeMethod('getPlatformVersion');
print('getPlatformVersion:' + result);
setState(() {
_systemVersion = result;
});
} on PlatformException catch (e) {
print(e.message);
}
}
....
//如果获取了natvie的数据就显示出来
- 原生向Flutter传递消息,通过EventChannel实现, 原生部分代码:
@Override
public void onEngineCreated() {
EventChannel methodChannel2 = new EventChannel(FlutterBoost.instance().engineProvider().getDartExecutor(), "native_flutter_channel");
methodChannel2.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
//当Flutter成功注册监听器的时候才会回调下面这句话,将msg传给flutter
events.success("msg");
}
@Override
public void onCancel(Object arguments) {
}
});
}
Flutter部分代码:
class SecondStatefulRouteWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SecondStatefulRouteWidgetState();
}
}
class _SecondStatefulRouteWidgetState extends State<SecondStatefulRouteWidget> {
static const eventPlugin = const EventChannel('native_flutter_channel');
var _streamSubscription;
@override
void initState() {
super.initState();
_streamSubscription = eventPlugin.receiveBroadcastStream().listen(_onData,
onError: _onError, onDone: _onDone, cancelOnError: true);
}
void _onData(Object event) {
// android传过来的是msg,如果输出的是msg就说明是成功啦
print("_onData_ " + event);
// 接收数据
setState(() {
});
}
void _onError(Object error) {
// 发生错误时被回调
print("_onData_ " + error);
setState(() {
//eventVal = "错误";
});
}
void _onDone() {
//结束时调用
print("_onData_ done" );
}
@override
void dispose() {
super.dispose();
if (_streamSubscription != null) {
_streamSubscription.cancel();
}
}
Flutter-boost优点
- 共享引擎,提升了原生和Flutter页面之间来回跳转的性能。
- 二级缓存策略: 内存中最多只保存2-3个截图,其余的写入文件按需加载。这样我们可以在保证用户体验的同时在内存方面也保持一个较为稳定的水平。
- 业界广泛使用,解决了很多FlutterModule所带有的细节问题,例如键盘、无障碍、状态栏、白屏等 。