一、背景
在Flutter开发中,除了热更新之外,Flutter 最受诟病的就是混合开发体验不好,而混合开发最重要的就是路由和组件生命周期的治理。
目前,Flutter 在跨平台计划一致性以及优良的体验曾经取得大多数开发者的统一称誉。但对于自身已有成熟的业务代码的我的项目来说,间接应用纯Flutter进行开发简直是不事实的,所以更多的我的项目是在不扭转更原有 App 业务的根底上,将 Flutter 作为子模块进行接入和开发,架构如下。
在混合开发中,就必然栈波及到 Flutter 页面与原生页面的跳转。而在Flutter 2.0之前,官网的路由计划在多引擎下有着通信隔离、资源不共享、以及可能带来极大的内存损耗(体现在关上多个 Flutter 页面时内存异样增长)等缺点。对于这种问题,业内呈现了flutter_boost 、 mix_stack 、 flutter_thrio等混合路由计划,其原理都是采纳单引擎复用计划,但这仍有不少痛点,次要体现在以下两点:
- 混合栈路由在应用时,仍有可能会产生内存异样;
- Flutter底层代码的批改,造成下层框架须要一直适配。
二、多引擎计划
在Flutter 2.0版本之前, Flutter 的控件渲染间接脱离了原生平台,也就是无论页面堆栈和渲染树都独立于平台运行,这诚然给 Flutter 带来了较好的跨平台体验,然而也造成了在和原生平台混合时开发的一些问题。
钻研过Flutter架构的童鞋都应该晓得,Flutter 的技术链路是建设在 C++ 编写的 Engine 和 Dart 编写的 Framework 层组成,如下图所示。
通过下面的架构图,咱们能够失去如下一些信息:
- Flutter 的Engine层治理着 Flutter 所应用到的四个线程,对于四个线程的介绍能够参考:Flutter的线程治理模型。
- Flutter的isolate 的作用是治理Dart 层的内存和单线程管制的运行实体,并且每个 isolate 之间的内存和逻辑是隔离的,对应着 Engine 也是资源不共享的。
- Engine依赖于原生视图组件提供渲染能力,对应着Activity/ViewController ,所以关上一个新的页面的时候就意味着会创立一个新的Engine,如果关上的页面够多,就可能会呈现内存的泄露。
并且,在混合路由治理方面,尽管 Dart 层自身有提供 navigator 等路由治理形式,但当咱们在原生工程中集成Flutter时,肯定会呈现 Native -> Flutter -> Native -> Flutter…
这种混合路由跳转的状况。
如何保留Flutter页面的状态,并且在页面回退或者跳转时复原Flutter页面的内容是多引擎计划一个比拟辣手的问题。下图演示了在Flutter多引擎计划下,因为Engine 的反复创立,带来的内存损耗的状况。
那多引擎计划在哪些场景中有劣势呢?次要体现在上面两个方面:
- 集成了 Flutter 界面的利用,其地位并不在路由栈的叶子节点上,且其可能是混合路由栈,即 Native -> Flutter -> Native -> Flutter。
- 多个 Flutter View 同时集成在同一个页面上,且同时显示。
三、 FlutterEngineGroup计划
3.1 FlutterEngineGroup
FlutterEngineGroup是Flutter 2.0提出的一个全新的计划,FlutterEngineGroup 计划应用的多个 Engine 的混合模式,大幅缩小了额定的 Flutter 引擎的内存占用。依据官网的数据,FlutterEngineGroup从 Android 上约 19MB,iOS上约13MB,降至约180kB,将固定的内存开销缩小了约 99% 。
并且,从 Flutter 官网提供的例子上看,FlutterEngineGroup 的 API 非常简略,多个 Engine 实例的外部都是独立保护本人的外部导航堆栈,所以能够做到每个 Engine 对应一个独立的模块。
得益于FlutterEngineGroup 生成的 FlutterEngine 能够共享 GPU 上下文、共享渲染线程等个性 ,因而能够更快的实现引擎的初始化和更低的内存占用。上面是应用运行官网的例子关上10个页面,在Android真机上,内存、CPU的应用状况。
3.2 运行官网示例
官网提供了一个FlutterEngineGroup应用例子,能够从FlutterEngineGroup下载例子,而后依照上面的步骤来运行例子:
cd ../multiple_flutters_moduleflutter pub getcd -open -a "Android Studio" multiple_flutters_android/
而后,在Android Studio关上并运行Android混合工程即可。
3.3 FlutterEngineGroup根本应用
从Flutter 2.0版本开始,FlutterEngine都将由 FlutterEngineGroup 去生成,生成的 FlutterEngine 能够独立利用于 FlutterActivity/FlutterViewController,甚至是 FlutterFragment。因而,应用FlutterEngineGroup之前须要先创立一个 FlutterEngineGroup对象,最好在Application的onCreate()办法中创立。
private void initFlutterEngine() { engineGroup = new FlutterEngineGroup(this); DartExecutor.DartEntrypoint dartEntrypoint = new DartExecutor.DartEntrypoint( FlutterInjector.instance().flutterLoader().findAppBundlePath(), "topMain" ); FlutterEngine topEngine = engineGroup.createAndRunEngine(this, dartEntrypoint); FlutterEngineCache.getInstance().put("topMain", topEngine); }
下面的topMain就是是跳转Flutter 须要执行的办法。而后,在原生页面应用Intent形式跳转到Flutter主页面,跳转时留神应用withCachedEngine,能够解决跳转的白屏问题。
Intent intent = FlutterActivity.withCachedEngine("topMain").build(getActivity()); startActivity(intent);
而后,在Flutter模块的main.dart创立一个DartEntrypoint对象,留神办法名须要和FlutterEngineGroup创立的FlutterEngine进行对应,如下所示。
@pragma('vm:entry-point')void topMain() => runApp(MyApp(color: Colors.green));
通过下面创立的 dartEntrypoint 和 context ,应用 FlutterEngineGroup 就能够创立出对应的 FlutterEngine ,其实在外部就是通过FlutterJNI.nativeSpawn 和原有的引擎交互,失去新的地址id。最初,利用生成的 FlutterEngine 的 binaryMessenger 来失去一个 MethodChannel 用于原生和 dart 之间的通信。
并且,通过上述流程失去的 Engine,能够间接用于渲染运行新的 Flutter UI,因为FlutterEngineGroup去治理Engine,所以咱们无需放心内存问题。
到这里,你或者曾经留神到,因为每个 Flutter 页面都是一个独立的 Engine ,因为 dart isolate 的设计理念,每个独立 Engine 的 Flutter 页面内存是无奈共享的。如果须要共享数据,那么只能在原生层持有数据,而后注入或者传递到每个 Flutter 页面中。正如官网所说的,每个 Flutter 页面更像是一个独立 Flutter 模块。
接下来,咱们会介绍在在混合开发中,Flutter 2.x如何进行数据的传递等基本操作。