无痛上手!!!Flutter 2.0 下混合开发浅析……

最新版的flutter不知道大家上手了没有,这里逛博客看见了一个大佬的分享,感觉很不错,大家可以看看,希望对大家的学习和工作有所帮助。
原文地址:https://blog.csdn.net/ZuoYueLiang/article/details/114987640

多余的前言

Flutter 2.0 发布时,其中最受大家关注之一的内容就是 Add-to-App 相关的更新,因为除了热更新之外,Flutter 最受大家诟病的就是混合开发体验不好。

为什么不好呢?因为 Flutter 的控件渲染直接脱离了原生平台,也就是无论页面堆栈和渲染树都独立于平台运行,这固然给 Flutter 带来了较好的跨平台体验,但是也造成了在和原生平台混合时存在高成本的问题。

且不说在已有的原生项目中集成 Flutter ,就是现阶段在 Flutter 中集成原生控件的 PlatformView 和 Hybrid Composition 体验也是有待提升,当然“有支持”和“能用”就已经是很不错的进展。

所以 Flutter 2.0 在千呼万唤中发布了 FlutterEngineGroup 用于支持官方的 Add Flutter to existing app 方案。

在此方案出现之前,类似的第三方支持有 flutter_boost 、 mix_stack 、 flutter_thrio 等等 ,它们是否好用这里不讨论,但是这些方案都要面对的问题是:

非官方的支持必然存在每个版本需要适配的问题,而按照 Flutter 目前的 issue closed 和 pr merge 的速度,很可能每个季度的版本都存在较大的变动,所以如果开发者不维护或者维护不及时,那么侵入性极强的这类框架很容易就成为项目的瓶颈。

而官方提供的 FlutterEngineGroup 方案有没有缺陷?肯定有的,它目前看起来更像是被催生出来的状态,各方面的问题还是有的,比如某些地方还存在不能 destroy 的问题。 (当然这个问题以及在 master 分支 merge 了)

但是官方提供的方案,就意味着这个设计得到了 Flutter 官方的保证,在未来的版本中会有兼容的优势。

FlutterEngineGroup 方案使用了多 Engine 混合模式,官方宣称除了一个 Engine 对象之外,后续每个 Engine 对象在 Android 和 iOS 上仅占用 180kB 。

以前的方案每多一个Engine ,可能就会多出 19MB Android 和 13MB iOS 的占用。

从 Flutter 官方提供的例子上看,FlutterEngineGroup 的 API 十分简单,多个 Engine 实例的内部都是独立维护自己的内部导航堆栈,所以可以做到每个 Engine 对应一个独立的模块。

所以使用 FlutterEngineGroup 之后,FlutterEngine 都将由 FlutterEngineGroup 去生成,生成的 FlutterEngine 可以独立应用于 FlutterActivity/FlutterViewController,甚至是 FlutterFragment :

所以就像例子上所示,你可以在一个 Activity 上显示两个独立的 FlutterView 。

这其实得益于通过 FlutterEngineGroup 生成的 FlutterEngine 可以共享 GPU 上下文, font metrics 和 isolate group snapshot ,从而实现了更快的初始速度和更低的内存占用。

下图是使用官方实例打开16个页面之后的内存使用情况,并且每个页面成功返回且没有出现黑屏。

简单的使用介绍

使用 FlutterEngineGroup 首先需要创建一个 FlutterEngineGroup 单例对象,之后每当需要创建 Engine 时,就通过它的 createAndRunEngine(activity, dartEntrypoint) 来创建对应的 FlutterEngine 。

        val app = activity.applicationContext as App
        // This has to be lazy to avoid creation before the FlutterEngineGroup.
        val dartEntrypoint =
            DartExecutor.DartEntrypoint(
                FlutterInjector.instance().flutterLoader().findAppBundlePath(), entrypoint
            )
        engine = app.engines.createAndRunEngine(activity, dartEntrypoint)
        this.delegate = delegate
        channel = MethodChannel(engine.dartExecutor.binaryMessenger, "multiple-flutters")

以官方 Demo 的这段代码为例子:

1、首先通过 findAppBundlePath 和 entrypoint 创建出 DartEntrypoint 对象,这里的 findAppBundlePath 主要就是默认的 flutter_assets 目录;而 entrypoint 其实就是 dart 代码里启动方法的名称;也就是绑定了在 dart 中 runApp 的方法。


///kotlin
app.engines.createAndRunEngine(pathToBundle, "topMain")


///dart
@pragma('vm:entry-point')
void topMain() => runApp(MyApp());

2、通过上面创建的 dartEntrypoint 和 context ,使用 FlutterEngineGroup 就可以创建出对应的 FlutterEngine ,其实在内部就是通过FlutterJNI.nativeSpawn 和原有的引擎交互,得到新的 Long 地址 id。

在 C++ 层类似于原有的 RunBundleAndSnapshotFromLibrary 方法,但是它不能更改包路径或者 asset ,所以只能加载同一份 AOT 文件,这里得到的指针地址就是一个新的 AndroidShellHolder 。

3、最后利用生成的 FlutterEngine 的 binaryMessenger 来得到一个 MethodChannel 用于原生和 dart 之间的通信。

通过上述流程得到的 Engine ,自然就可以直接用于渲染运行新的 Flutter UI,比如直接继承 FlutterActivity ,然后 override provideFlutterEngine 方法返回得到的 Engine 。


class SingleFlutterActivity : FlutterActivity()

    ·······

    override fun provideFlutterEngine(context: Context): FlutterEngine? {
        return engine
    }


}

是不是很简单?这么简单的接入后:

  • 在 dart 层面可以通过 MethodChannel 打开原始页面;
  • 在原生层可以通过新建 FlutterEngine 打开新的 Flutter 页面;
  • 甚至你还可以在原生层打开一个 FlutterView 的 Dialog;

当然,到这里你可能已经注意到了,因为每个 Flutter 页面都是一个独立的 Engine ,由于 dart isolate 的设计理念,每个独立 Engine 的 Flutter 页面内存是无法共享的。

也就是说,当你需要共享数据时,只能在原生层持有数据,然后注入或者传递到每个 Flutter 页面中,就像官方所说的,每个 Flutter 页面更像是一个独立 Flutter 模块。

当然这也造成了一些不必要的麻烦,比如:同一张图片,在原生层、不同 Flutter Engine 会出现多次加载的问题,这种问题可能就需要你针对 Flutter 的图片加载使用外界纹理,来实现在原生层统一的内存管理等。

另外目前我发现问题还有: Android 11 上的 ARM TBI 问题 ,不过通过这次尝试,相信 FlutterEngineGroup 的进展将会越来越明朗,更早的被应用到生产环境中。

最后

博主做Android开发六年了,现在越来越感觉混合开发才是移动开发的大趋势,也一直在坚持学习,现在也算是小有收获,下面是一个资料合集整理,他们给过我很多帮助,希望能帮到正在学习这个方向技术的朋友。

这里给大家免费分享一套Flutter的学习资料,感兴趣的朋友可以
点击此处蓝色字体,可以直达我们两千人的技术交流圈。共享文件夹自取(密码找管理员)

Flutter进阶学习笔记

整理大纲

  • 为什么Flutter是跨平台开发的终极之选
  • 在Windows上搭建Flutter开发环境
  • 编写您的第一个 Flutter App
  • Flutter开发环境搭建和调试
  • Dart语法篇之基础语法(一)
  • Dart语法篇之集合的使用与源码解析(二)
  • Dart语法篇之集合操作符函数与源码分析(三)
  • Dart语法篇之函数的使用(四)
  • Dart语法篇之面向对象基础(五)
  • Dart语法篇之面向对象继承和Mixins(六)
  • Dart语法篇之类型系统与泛型(七)
  • Flutter中的widget

点击此处蓝色字体,里面有免费获取通道。全共享文件夹自取

为什么Flutter是跨平台开发的终极之选

  • 这是为什么?
  • 跨平台开发
  • 什么是 Flutter
  • Flutter 的特性
  • Flutter 构建应用的工具
  • 使用 Flutter 构建的热门应用
  • 构建 Flutter 应用的成本
  • ……

在Windows上搭建Flutter开发环境

  • 使用镜像
  • 系统要求
  • 获取Flutter SDK
  • 编辑器设置
  • Android设置
  • 起步: 配置编辑器
  • 起步: 体验
  • 体验热重载
  • 创建新的应用
  • 运行应用程序
  • ……

第三章 编写您的第一个 Flutter App

  • 第1步: 创建 Flutter app
  • 第2步: 使用外部包(package)
  • 第3步: 添加一个 有状态的部件(Stateful widget)
  • 第4步: 创建一个无限滚动ListView
  • 第5步: 添加交互
  • 第6步: 导航到新页面
  • 第7步:使用主题更改UI
  • ……

第四章 Flutter开发环境搭建和调试

  • 开发环境的搭建
  • 模拟器的安装与调试
  • 开发环境的搭建
  • 模拟器的安装与调试
  • ……

第五章 Dart语法篇之基础语法(一)

  • Hello Dart
  • 数据类型
  • 变量和常量
  • 集合(List、Set、Map)
  • 流程控制
  • 运算符
  • 异常
  • 函数
  • ……

点击此处蓝色字体,直达我们两千人的技术交流圈。共享文件夹自取,密码找管理员

第六章 Dart语法篇之集合的使用与源码解析(二)

  • List
  • Set
  • Map
  • Queue
  • LinkedList
  • HashMap
  • Map、HashMap、LinkedHashMap、SplayTreeMap区别
  • 命名构造函数from和of的区别以及使用建议
  • ……

第七章 Dart语法篇之集合操作符函数与源码分析(三)

  • Iterable(Iterable类关系图、Iterable类方法图……)
  • forEach(介绍、使用方式、源码解析……)
  • map(介绍、使用方式、源码解析……)
  • any(介绍、使用方式、源码解析……)
  • every(介绍、使用方式、源码解析……)
  • where(介绍、使用方式、源码解析……)
  • firstWhere和singleWhere和lastWhere(介绍、使用方式、源码解析……)
  • join(介绍、使用方式、源码解析……)
  • take(介绍、使用方式、源码解析……)
  • takeWhile(介绍、使用方式、源码解析……)
  • ……(内容太多)

第八章 Dart语法篇之函数的使用(四)

  • 函数参数
  • 匿名函数(闭包,lambda)
  • 箭头函数
  • 局部函数
  • 顶层函数和静态函数
  • main函数
  • ……

第九章 Dart语法篇之面向对象基础(五)

  • 属性访问器(accessor)函数setter和getter
  • 面向对象中的变量
  • 构造函数
  • 抽象方法、抽象类和接口
  • 类函数
  • ……

第十章 Dart语法篇之面向对象继承和Mixins(六)

  • 类的单继承
  • 基于Mixins的多继承
  • ……

第十一章 Dart语法篇之类型系统与泛型(七)

  • 可选类型
  • 接口类型
  • 泛型
  • 类型具体化
  • ……

第十二章 Flutter中的widget

  • Flutter页面-基础Widget
  • Widget
  • StatelessWidget
  • Stateful Widget
  • State生命周期
  • 基础widget
  • 文本显示
  • ……

点击此处蓝色字体,直达获取。

猜你喜欢

转载自blog.csdn.net/BUGgogogo/article/details/115097632
今日推荐