Flutter 作为 Google 开源的新一代跨平台、高性能 UI 框架,旨在帮助开发者高效地构建出跨平台的、UI 与交互体验一致的精美应用,推出后一直倍受开发者的青睐。
当需要开发一个全新的应用时,我们可以很方便地从零开始,完全使用 Flutter 进行开发。但如果是针对一个现有的应用,需要引入 Flutter 技术,显然使用 Flutter 全部重写一遍是不现实的。幸运的是,Flutter 很好地支持了以独立页面、甚至是 UI 片段的方式集成到现有的应用中,即所谓的混合开发模式。本文主要从一个 Android 开发的视角,谈谈 Android 平台下, Flutter 的混合开发与构建。
相信现在应该很少会有移动端开发者不知道 Flutter,这里不再做过多介绍。对于这门技术,使用过的应该绝大多数都会说好;没用过的推荐尝试一下,跑个 Demo 体验体验,有可能它就是你需要学习和掌握的最后一门新技术了。回过头来,Flutter 究竟有什么独特的魅力让它能从一众技术中脱颖而出呢?总结一下,主要有以下几点:
受益于以上的核心优势,Flutter 推出后圈了很多移动开发者的粉,各互联网大厂也纷纷将其作为一项基础技术进行研究。在 Flutter 初期,其应用场景主要是从 0 构建一个全新 App,对混合开发的支持很不友好。但作为一门跨平台的技术框架,到底还是需要依赖原生平台提供的诸多系统能力,此外还有众多现存原生 App 跃跃欲试,因此在这个需求背景下,混合开发的支持与完善至今已发展得越来越好,下面我们就用一个简单的示例开始 Android 端的 Flutter 混合开发与构建之旅。
要在一个已有的 Android Project 中使用 Flutter,需要引入一个 Flutter Module。在 Android Studio(需要确保 Flutter 插件已经成功安装并启用)中打开现有 Android 工程,通过使用 File > New > New Module… 菜单,我们可以新创建一个 Flutter 模块或是导入一个外部的 Flutter 模块。
这里以最简单的 Android App 项目为例,导入 Flutter 模块。在 Flutter 模块导入成功之后,原工程文件、结构都会发生一些变化,主要有:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
‘flutter_module/.android/include_flutter.groovy‘
))
include ‘:flutter_module‘
project(‘:flutter_module‘).projectDir = new File(‘../flutter_module‘)
在引入 Flutter 模块之前,项目中仅有 app 一个 Module;而在引入之后,可以看到除了原有的 app Module 外,Flutter Gradle 插件自动引入了额外几个子 Module:
注意:一个工程不能包含多个 Flutter Module,最多只能引入一个,这是由 Flutter 的 Gradle 插件决定的。
完成 Flutter 模块的引入后,我们再来看看如何使用 Flutter。
首先需要在 App 模块的build.gradle脚本文件中添加对Flutter工程的依赖,只有这样 Flutter 模块才会参与到整个应用的构建中来,我们也才能够在 App 模块中调用到 Flutter 提供的 Java 层 API。如下所示:
dependencies {
implementation project(‘:flutter‘)
}
我们可以选择使用 Activity、Fragment 或者 View 来承载 Flutter 的 UI,这里主要介绍前面两种方式,并假设flutter_module中已经通过runApp方法渲染了一个widget。
FlutterActivity
.withNewEngine()
.build(context)
.also { startActivity(it) }
val flutterFragment = FlutterFragment.withNewEngine()
.dartEntrypoint(getDartEntrypointFunctionName())
.initialRoute(getInitialRoute())
.appBundlePath(getAppBundlePath())
.flutterShellArgs(FlutterShellArgs.fromIntent(intent))
.handleDeeplinking(shouldHandleDeeplinking())
.renderMode(renderMode)
.transparencyMode(transparencyMode)
.shouldAttachEngineToActivity(shouldAttachEngineToActivity())
.build<FlutterFragment>()
fragmentManager
.beginTransaction()
.add(
FRAGMENT_CONTAINER_ID,
flutterFragment,
TAG_FLUTTER_FRAGMENT
)
.commit()
channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter")
channel.setMethodCallHandler { call, _ ->
when (call.method) {
"incrementCounter" -> {
count++
channel.invokeMethod("reportCounter", count)
}
}
}
Dart 层使用相同的名称创建 MethodChannel,并设置 Handler 处理回调结果,随后调用 incrementCounter 方法请求 counter。示例代码如下:
final _channel = MethodChannel(‘dev.flutter.example/counter‘);
_channel.setMethodCallHandler(_handleMessage);
_channel.invokeMethod(‘incrementCounter‘);
Future<dynamic> _handleMessage(MethodCall call) async {
if (call.method == ‘reportCounter‘) {
_count = call.arguments as int;
notifyListeners();
}
}
这里我们是通过手动创建 MethodChannel 进行通信的,这在进行简单通信的场景是没问题的,但在通信接口 API 比较复杂的情况就不是很适用了。
一是繁琐,因为我们需要手写大量的打包、拆包代码;二是容易出错。这个时候就轮到 Pigeon 大显身手了。Pigeon 是一个官方推出的代码生成工具,可以生成类型安全的双向通信 API 接口,具体可以参考官方的 Example,这里不再赘述。
Pigeon :https://flutter.dev/docs/development/platform-integration/platform-channels#pigeon
到这里,我们已经了解了如何在现有 Android 项目中引入并使用 Flutter,接下来我们再来探究一下 Flutter APK 的结构,看看 Flutter Tools 在这个 APK 包内到底打包了哪些东西。下面两图分别为 Debub 模式和 Release 模式下构建出来的 Flutter APK 包结构,忽略了非 Flutter 相关的项。
可以看到两个模式下的 APK 结构大致相同,说明如下:
这里,也总结了几个我们在应用的时候遇到的问题,供大家参考避坑。
Flutter 混合开发使得开发者可以渐进式地进行 Flutter 开发与迁移,是 Flutter 寄生于原生平台至关重要的一环。
本文主要从一个 Android 开发的视角,介绍了 Flutter 混合开发的入门知识。随着 Flutter 开源项目的不断迭代与演进,混合开发的体验正在变得越来越好、性能也越来越高。但美中不足的是仍然有一些应用场景与问题并未得到很好地完善与解决,比如 Flutter 多实例问题(我们也将在本月的另一篇文章中跟大家分享介绍我们在实践 Flutter 多实例遇到的问题与解决方案,敬请关注)。
瑕不掩瑜,Flutter 这门技术整体而言还是非常不错的,它如今仍处于快速发展的阶段,相信在 Flutter 团队与开源社区的共同努力下,未来的生态值得期待。
李成达,网易云信资深移动端开发工程师,热衷于研究跨平台开发技术以及工程提效,目前主要负责视频会议组件化 SDK 的相关研发工作。
原文:https://www.cnblogs.com/wangyiyunxin/p/14790567.html