请选择 进入手机版 | 继续访问电脑版

[IOS] Flutter Boost 混合开辟 框架

[复制链接]
查看104 | 回复25 | 2021-9-13 16:39:24 | 显示全部楼层 |阅读模式
目次

一、Flutter Boost简介

众所周知,Flutter是一个由C++实现的Flutter Engine和由Dart实现的Framework构成 的跨平台技术框架。此中 ,Flutter Engine负责线程管理、Dart VM状态管理以及Dart代码加载等工作,而Dart代码所实现的Framework则负责上层业务开辟 ,如Flutter提供的组件等概念就是Framework的范畴。

随着Flutter的发展,国内越来越多的App开始接入Flutter。为了降低风险,大部分App采用渐进式方式引入Flutter,在App里选几个页面用Flutter来编写,但都碰到 了类似 的题目 ,在原生页面和Flutter页面共存的环境 下,怎样 管理路由,以及原生页面与Flutter页面之间的切换和通讯 都是混合开辟 中必要 办理 的题目 。然而,官方没有提供明白 的办理 方案,只是在混合开辟 时,官方建议开辟 者,应该利用 同一个引擎支持多窗口绘制的本领 ,至少在逻辑上做到FlutterViewController是共享同一个引擎内里 的资源。换句话说,官方盼望 全部 的绘制窗口共享同一个主Isolate,而不是出现多个主Isolate的环境 。不过,对于现在 已经出现的多引擎模式题目 ,Flutter官方也没有提供好的办理 方案。除了内存斲丧 严峻 外,多引擎模式还会带来如下一些题目

  • 冗余资源题目 。多引擎模式下每个引擎的Isolate是相互独立的,固然 在逻辑上这并没有什么坏处,但是每个引擎底层都维护了一套图片缓存等比较斲丧 内存的对象,因此装备 的内存斲丧 黑白 常严峻 的。
  • 插件注册题目 。在Flutter插件中,消息传递必要 依靠 Messenger,而Messenger是由FlutterViewController去实现的。假如 一个应用中同时存在多个FlutterViewController,那么插件的注册和通讯 将会变得混乱且难以维护。
  • Flutter组件和原生页面的差异化题目 。通常,Flutter页面是由组件构成的,原生页面则是由ViewController或者Activity构成的。逻辑上来说,我们盼望 消除Flutter页面与原生页面的差异,否则在举行 页面埋点和别的 一些操作时增长 一些额外的工作量。
  • 增长 页面通讯 的复杂度。假如 全部 的Dart代码都运行在同一个引擎实例中,那么它们会共享同一个Isolate,可以用同一 的框架完成组件之间的通讯 ,但是假如 存在多个引擎实例会让Isolate的管理变得更加复杂。

假如 不办理 多引擎题目 ,那么混合项目标 导航栈如下图所示。

Flutter Boost 混合开辟
框架

现在 ,对于原生工程混编Flutter工程出现的多引擎模式题目 ,国内紧张 有两种办理 方案,一种是字节跳动的修改Flutter Engine源码方案,另一种是闲鱼开源的FlutterBoost。由于字节跳动的混合开辟 的方案没有开源,以是 现在 能利用 的就剩下FlutterBoost方案。

FlutterBoost是闲鱼技术团队开辟 的一个可复用页面的插件,旨在把Flutter容器做成类似 于欣赏 器的加载方案。为此,闲鱼技术团队为盼望 FlutterBoost能完成如下的基本功能:

  • 可复用的通用型混合开辟 方案。
  • 支持更加复杂的混合模式,比如支持Tab切换的场景。
  • 无侵入性方案,利用 时不再依靠 修改Flutter的方案。
  • 支持对页面生命周期举行 同一 的管理。
  • 具有同一 明白 的计划 概念。

并且,迩来 Flutter Boost升级了3.0版本,并带来了如下的一些更新:

  • 不侵入引擎,兼容Flutter的各种版本,Flutter sdk的升级不必要 再升级FlutterBoost,极大降低升级成本。
  • 不区分Androidx和Support分支。
  • 简化架构和接口,和FlutterBoost2.0比,代码减少了一半。
  • 双端同一 ,包括接口和计划 上的同一 。
  • 支持打开Flutter页面,不再打开容器场景。
  • 页面生命周期变化关照 更方便业务利用 。
  • 办理 了2.0中的遗留题目 ,比方 ,Fragment接入困难、页面关闭后不能传递数据、dispose不实验 ,内存占用过高等。

二、Flutter Boost集成

在原生项目中集成Flutter Boost只必要 将Flutter Boost当作 是一个插件工程即可。和其他Flutter插件的集成方式一样,利用 FlutterBoost之前必要 先添加依靠 。利用 Android Studio打开混合工程的Flutter工程,在pubspec.yaml中添加FlutterBoost依靠 插件,如下所示。

  1. flutter_boost:
  2. git:
  3. url: 'https://github.com/alibaba/flutter_boost.git'
  4. ref: 'v3.0-hotfixes'
复制代码

必要 阐明 的是,此处的所依靠 的FlutterBoost的版本与Flutter的版本是对应的,假如 不对应利用 过程中会出现版本不匹配的错误。然后,利用 flutter packages get下令 将FlutterBoost插件拉取到本地。

2.1 Android集成

利用 Android Studio打开新建的原生Android工程,在原生Android工程的settings.gradle文件中添加如下代码。

  1. setBinding(new Binding([gradle: this]))
  2. evaluate(new File(
  3. settingsDir.parentFile,
  4. 'flutter_library/.android/include_flutter.groovy'))
复制代码

然后,打开原生Android工程app目次 下的build.gradle文件,继续添加如下依靠 脚本。

  1. dependencies {
  2. implementation project(':flutter_boost')
  3. implementation project(':flutter')
  4. }
复制代码

重新编译构建原生Android工程,假如 没有任何错误则阐明 Android成功了集成FlutterBoost。利用 Flutter Boost 之前,必要 先实验 初始化。打开原生Android工程,新建一个继承FlutterApplication的Application,然后在onCreate()方法中初始化FlutterBoost,代码如下。

  1. public class MyApplication extends FlutterApplication {
  2. @Override
  3. public void onCreate() {
  4. super.onCreate();
  5. FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
  6. @Override
  7. public void pushNativeRoute(String pageName, HashMap<String, String> arguments) {
  8. Intent intent = new Intent(FlutterBoost.instance().currentActivity(), NativePageActivity.class);
  9. FlutterBoost.instance().currentActivity().startActivity(intent);
  10. }
  11. @Override
  12. public void pushFlutterRoute(String pageName, HashMap<String, String> arguments) {
  13. Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class, FlutterBoost.ENGINE_ID)
  14. .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
  15. .destroyEngineWithActivity(false)
  16. .url(pageName)
  17. .urlParams(arguments)
  18. .build(FlutterBoost.instance().currentActivity());
  19. FlutterBoost.instance().currentActivity().startActivity(intent);
  20. }
  21. },engine->{
  22. engine.getPlugins();
  23. } );
  24. }
  25. }
复制代码

然后,打开原生Android工程下的AndroidManifest.xml文件,将Application更换 成自定义的MyApplication,如下所示。

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. package="com.idlefish.flutterboost.example">
  4. <application
  5. android:name="com.idlefish.flutterboost.example.MyApplication"
  6. android:label="flutter_boost_example"
  7. android:icon="@mipmap/ic_launcher">
  8. <activity
  9. android:name="com.idlefish.flutterboost.containers.FlutterBoostActivity"
  10. android:theme="@style/Theme.AppCompat"
  11. android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
  12. android:hardwareAccelerated="true"
  13. android:windowSoftInputMode="adjustResize" >
  14. <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background"/>
  15. </activity>
  16. <meta-data android:name="flutterEmbedding"
  17. android:value="2">
  18. </meta-data>
  19. </application>
  20. </manifest>
复制代码

由于Flutter Boost 是以插件的方式集成到原生Android项目标 ,以是 我们可以在Native 打开和关闭Flutter模块的页面。

  1. FlutterBoost.instance().open("flutterPage",params);
  2. FlutterBoost.instance().close("uniqueId");
复制代码

而Flutter Dart的利用 如下。起首 ,我们可以在main.dart文件的程序入口main()方法中举行 初始化。

  1. void main() {
  2. runApp(MyApp());
  3. }
  4. class MyApp extends StatefulWidget {
  5. @override
  6. _MyAppState createState() => _MyAppState();
  7. }
  8. class _MyAppState extends State<MyApp> {
  9. static Map<String, FlutterBoostRouteFactory>
  10. routerMap = {
  11. '/': (settings, uniqueId) {
  12. return PageRouteBuilder<dynamic>(
  13. settings: settings, pageBuilder: (_, __, ___)
  14. => Container());
  15. },
  16. 'embedded': (settings, uniqueId) {
  17. return PageRouteBuilder<dynamic>(
  18. settings: settings,
  19. pageBuilder: (_, __, ___) =>
  20. EmbeddedFirstRouteWidget());
  21. },
  22. 'presentFlutterPage': (settings, uniqueId) {
  23. return PageRouteBuilder<dynamic>(
  24. settings: settings,
  25. pageBuilder: (_, __, ___) =>
  26. FlutterRouteWidget(
  27. params: settings.arguments,
  28. uniqueId: uniqueId,
  29. ));
  30. }};
  31. Route<dynamic> routeFactory(RouteSettings settings, String uniqueId) {
  32. FlutterBoostRouteFactory func =routerMap[settings.name];
  33. if (func == null) {
  34. return null;
  35. }
  36. return func(settings, uniqueId);
  37. }
  38. @override
  39. void initState() {
  40. super.initState();
  41. }
  42. @override
  43. Widget build(BuildContext context) {
  44. return FlutterBoostApp(
  45. routeFactory
  46. );
  47. }
复制代码

当然,还可以监听页面的生命周期,如下所示。

  1. class SimpleWidget extends StatefulWidget {
  2. final Map params;
  3. final String messages;
  4. final String uniqueId;
  5. const SimpleWidget(this.uniqueId, this.params, this.messages);
  6. @override
  7. _SimpleWidgetState createState() => _SimpleWidgetState();
  8. }
  9. class _SimpleWidgetState extends State<SimpleWidget>
  10. with PageVisibilityObserver {
  11. static const String _kTag = 'xlog';
  12. @override
  13. void didChangeDependencies() {
  14. super.didChangeDependencies();
  15. print('$_kTag#didChangeDependencies, ${widget.uniqueId}, $this');
  16. }
  17. @override
  18. void initState() {
  19. super.initState();
  20. PageVisibilityBinding.instance.addObserver(this, ModalRoute.of(context));
  21. print('$_kTag#initState, ${widget.uniqueId}, $this');
  22. }
  23. @override
  24. void dispose() {
  25. PageVisibilityBinding.instance.removeObserver(this);
  26. print('$_kTag#dispose, ${widget.uniqueId}, $this');
  27. super.dispose();
  28. }
  29. @override
  30. void onForeground() {
  31. print('$_kTag#onForeground, ${widget.uniqueId}, $this');
  32. }
  33. @override
  34. void onBackground() {
  35. print('$_kTag#onBackground, ${widget.uniqueId}, $this');
  36. }
  37. @override
  38. void onAppear(ChangeReason reason) {
  39. print('$_kTag#onAppear, ${widget.uniqueId}, $reason, $this');
  40. }
  41. void onDisappear(ChangeReason reason) {
  42. print('$_kTag#onDisappear, ${widget.uniqueId}, $reason, $this');
  43. }
  44. @override
  45. Widget build(BuildContext context) {
  46. return Scaffold(
  47. appBar: AppBar(
  48. title: Text('tab_example'),
  49. ),
  50. body: SingleChildScrollView(
  51. physics: BouncingScrollPhysics(),
  52. child: Container(
  53. child: Column(
  54. crossAxisAlignment: CrossAxisAlignment.start,
  55. children: <Widget>[
  56. Container(
  57. margin: const EdgeInsets.only(top: 80.0),
  58. child: Text(
  59. widget.messages,
  60. style: TextStyle(fontSize: 28.0, color: Colors.blue),
  61. ),
  62. alignment: AlignmentDirectional.center,
  63. ),
  64. Container(
  65. margin: const EdgeInsets.only(top: 32.0),
  66. child: Text(
  67. widget.uniqueId,
  68. style: TextStyle(fontSize: 22.0, color: Colors.red),
  69. ),
  70. alignment: AlignmentDirectional.center,
  71. ),
  72. InkWell(
  73. child: Container(
  74. padding: const EdgeInsets.all(8.0),
  75. margin: const EdgeInsets.all(30.0),
  76. color: Colors.yellow,
  77. child: Text(
  78. 'open flutter page',
  79. style: TextStyle(fontSize: 22.0, color: Colors.black),
  80. )),
  81. onTap: () => BoostNavigator.of().push("flutterPage",
  82. arguments: <String, String>{'from': widget.uniqueId}),
  83. )
  84. Container(
  85. height: 300,
  86. width: 200,
  87. child: Text(
  88. '',
  89. style: TextStyle(fontSize: 22.0, color: Colors.black),
  90. ),
  91. )
  92. ],
  93. ))),
  94. );
  95. }
  96. }
复制代码

然后,运行项目,就可以从原生页面跳转到Flutter页面,如下图所示结果 。

Flutter Boost 混合开辟
框架

2.2 iOS集成

和Android的集成步骤一样,利用 Xcode打开原生iOS工程,然后在iOS的AppDelegate文件中初始化Flutter Boost ,如下所示。

  1. @interface AppDelegate ()
  2. @end
  3. @implementation AppDelegate
  4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  5. {
  6. MyFlutterBoostDelegate* delegate=[[MyFlutterBoostDelegate alloc ] init];
  7. [[FlutterBoost instance] setup:application delegate:delegate callback:^(FlutterEngine *engine) {
  8. } ];
  9. return YES;
  10. }
  11. @end
复制代码

下面是自定义的FlutterBoostDelegate的代码,如下所示。

  1. @interface MyFlutterBoostDelegate : NSObject<FlutterBoostDelegate>
  2. @property (nonatomic,strong) UINavigationController *navigationController;
  3. @end
  4. @implementation MyFlutterBoostDelegate
  5. - (void) pushNativeRoute:(FBCommonParams*) params{
  6. BOOL animated = [params.arguments[@"animated"] boolValue];
  7. BOOL present= [params.arguments[@"present"] boolValue];
  8. UIViewControllerDemo *nvc = [[UIViewControllerDemo alloc] initWithNibName:@"UIViewControllerDemo" bundle:[NSBundle mainBundle]];
  9. if(present){
  10. [self.navigationController presentViewController:nvc animated:animated completion:^{
  11. }];
  12. }else{
  13. [self.navigationController pushViewController:nvc animated:animated];
  14. }
  15. }
  16. - (void) pushFlutterRoute:(FBCommonParams*)params {
  17. FlutterEngine* engine = [[FlutterBoost instance ] getEngine];
  18. engine.viewController = nil;
  19. FBFlutterViewContainer *vc = FBFlutterViewContainer.new ;
  20. [vc setName:params.pageName params:params.arguments];
  21. BOOL animated = [params.arguments[@"animated"] boolValue];
  22. BOOL present= [params.arguments[@"present"] boolValue];
  23. if(present){
  24. [self.navigationController presentViewController:vc animated:animated completion:^{
  25. }];
  26. }else{
  27. [self.navigationController pushViewController:vc animated:animated];
  28. }
  29. }
  30. - (void) popRoute:(FBCommonParams*)params
  31. result:(NSDictionary *)result{
  32. FBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController;
  33. if([vc isKindOfClass:FBFlutterViewContainer.class] && [vc.uniqueIDString isEqual: params.uniqueId]){
  34. [vc dismissViewControllerAnimated:YES completion:^{}];
  35. }else{
  36. [self.navigationController popViewControllerAnimated:YES];
  37. }
  38. }
  39. @end
复制代码

假如 要在原生iOS代码中打开或关闭Flutter页面,可以利用 下面的方式。

  1. [[FlutterBoost instance] open:@"flutterPage" arguments:@{@"animated":@(YES)} ];
  2. [[FlutterBoost instance] open:@"secondStateful" arguments:@{@"present":@(YES)}];
复制代码

三、Flutter Boost架构

对于混合工程来说,原生端和Flutter端对于页面的定义是不一样的。对于原生端而言,页面通常指的是一个ViewController或者Activity,而对于Flutter来说,页面通常指的是Flutter组件。FlutterBoost框架所要做的就是同一 混合工程中页面的概念,或者说弱化Flutter组件对应容器页面的概念。换句话说,当有一个原生页面存在的时间 ,FlutteBoost就能保证肯定 有一个对应的Flutter的容器页面存在。

FlutterBoost框架实在 就是由原生容器通过消息驱动Flutter页面目面貌 器,从而达到原生容器与Flutter容器同步的目标 ,而Flutter渲染的内容是由原生容器去驱动的,下面是Flutter Boost 给的一个Flutter Boost 的架构表示 图。

Flutter Boost 混合开辟
框架

可以看到,Flutter Boost插件分为平台和Dart两头 ,中心 通过Message Channel毗连 。平台侧提供了Flutter引擎的设置 和管理、Native容器的创建/烧毁 、页面可见性变化关照 ,以及Flutter页面的打开/关闭接口等。而Dart侧除了提供类似 原生Navigator的页面导航接口的本领 外,还负责Flutter页面的路由管理。

总的来说,正是基于共享同一个引擎的方案,使得FlutterBoost框架有用 的办理 了多引擎的题目 。简单来说,FlutterBoost在Dart端引入了容器的概念,当存在多个Flutter页面时,FlutterBoost不必要 再用栈的布局 去维护现有页面,而是利用 扁平化键值对映射的情势 去维护当前全部 的页面,并且每个页面拥有一个唯一的id

四、FlutterBoost3.0更新

4.1 不入侵引擎

为了办理 官方引擎复用引起的题目 ,FlutterBoost2.0拷贝了Flutter引擎Embedding层的一些代码举行 改造,这使得后期的升级成本极高。而FlutterBoost3.0采用继承的方式扩展FlutterActivity/FlutterFragment等组件的本领 ,并且通过在得当 机遇 给Dart侧发送appIsResumed消息办理 引擎复用时生命周期变乱 错乱导致的页面卡死题目 ,并且,FlutterBoost 3.0 也兼容最新的官方发布的 Flutter 2.0。

4.2 不区分Androidx和Support分支

FlutterBoost2.0通过本身 实现FlutterActivityAndFragmentDelegate.Host接口来扩展FlutterActivity和FlutterFragment的本领 ,而getLifecycle是必须实现的接口,这就导致对androidx的依靠 。这也是为什么FlutterBoostView的实现没有被放入FlutterBoost3.0插件中的缘故原由 。而FlutterBoost3.0通过继承的方式扩展FlutterActivity/FlutterFragment的本领 的额外收益就是,可以做到不依靠 androidx。

4.3 双端计划 同一 ,接口同一

很多Flutter开辟 者只会一端,只会Android 或者只会IOS,但他必要 接入双端,以是 双端同一 能降低他的 学习成本和接入成本。FlutterBoost3.0,在计划 上 Android和IOS都做了对齐,特别 接口上做到了参数级的对齐。

4.4 支持 【打开flutter页面不再打开容器】 场景

在Flutter模块内部,Flutter 页面跳转Flutter 页面是可以不必要 再打开Flutter容器的,不打开容器,能节省 内存开销。在FlutterBoost3.0上,打开容器和不打开容器的区别表现在 用户接口上仅仅是withContainer参数是否为true就好。

  1. InkWell(
  2. child: Container(
  3. color: Colors.yellow,
  4. child: Text(
  5. '打开外部路由',
  6. style: TextStyle(fontSize: 22.0, color: Colors.black),
  7. )),
  8. onTap: () => BoostNavigator.of().push("flutterPage",
  9. arguments: <String, String>{'from': widget.uniqueId}),
  10. ),
  11. InkWell(
  12. child: Container(
  13. color: Colors.yellow,
  14. child: Text(
  15. '打开内部路由',
  16. style: TextStyle(fontSize: 22.0, color: Colors.black),
  17. )),
  18. onTap: () => BoostNavigator.of().push("flutterPage",
  19. withContainer: true,
  20. arguments: <String, String>{'from': widget.uniqueId}),
  21. )
复制代码

4.5 生命周期的精准关照

在FlutterBoost2.0上,每个页面都会收到页面生命周期关照 ,而FlutterBoost3.0只会关照 页面可见性现实 发生了变化的页面,接口也更符合flutter的计划 。

4.6 其他Issue

除了上面的一些特性外,Flutter Boost 3.0版本还办理 了如下一些题目 :

  • 页面关闭后参数的传递,之前只有iOS支持,android不支持,现在 在dart侧实现,Ios 和Android 都支持。
  • 办理 了Android 状态栏字体和颜色题目 。
  • 办理 了页面回退willpopscope不起作用题目 。
  • 办理 了不在栈顶的页面也收到生命周期回调的题目
  • 办理 了多次setState耗性能题目 。
  • 提供了Framgent 多种接入方式的Demo,方便tab 场景的接入。
  • 生命周期的回调代码,可以用户代码内里 with的方式接入,利用 更简单。
  • 全面简化了,接入成本,包括 dart侧,android侧和ios
  • 丰富了demo,包含了基本场景,方便用户接入 和测试回归

到此这篇关于Flutter Boost 混合开辟 框架的文章就先容 到这了,更多相干 Flutter Boost内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的相干 文章盼望 大家以后多多支持脚本之家!,盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 123457080 | 2021-9-16 19:49:45 | 显示全部楼层
好多兽医在广场上义诊,admin楼主去看看吧!
回复

使用道具 举报

avatar 上山打老虎667 | 2021-9-18 23:49:47 | 显示全部楼层
admin楼主,你妈妈喊你回家吃药!
回复

使用道具 举报

avatar 尘埃416 | 2021-9-18 23:49:52 | 显示全部楼层
信admin楼主,考试不挂科!
回复

使用道具 举报

avatar 千山落月坝 | 2021-9-24 09:37:34 | 显示全部楼层
什么狗屁帖子啊,admin楼主的语文是苍老师教的吗?
回复

使用道具 举报

avatar 身无分文的少年 | 2021-10-16 19:01:54 | 显示全部楼层
青春不在了,青春痘还在!
回复

使用道具 举报

avatar 白云追月素 | 2021-10-18 05:50:07 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的网站,运动刷步数还是免费刷的,QQ和微信都可以刷,特别好用。访问地址:http://yd.mxswl.com 猫先森网络
回复

使用道具 举报

avatar 哲911 | 2021-10-19 06:21:27 | 显示全部楼层
关注一下!
回复

使用道具 举报

avatar 123457125 | 2021-10-19 08:29:33 | 显示全部楼层
最近压力山大啊!
回复

使用道具 举报

大神就是大神,这么经典!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则