引言
Flutter 作为 Google 开源的新一代跨平台、高性能 UI 框架,旨在帮忙开发者高效地构建出跨平台的、UI 与交互体验统一的精美利用,推出后始终倍受开发者的青眼。
当须要开发一个全新的利用时,咱们能够很不便地从零开始,齐全应用 Flutter 进行开发。但如果是针对一个现有的利用,须要引入 Flutter 技术,显然应用 Flutter 全副重写一遍是不事实的。侥幸的是,Flutter 很好地反对了以独立页面、甚至是 UI 片段的形式集成到现有的利用中,即所谓的混合开发模式。 本文次要从一个 Android 开发的视角,谈谈 Android 平台下,Flutter 的混合开发与构建。
Hello Flutter
置信当初应该很少会有挪动端开发者不晓得 Flutter,这里不再做过多介绍。对于这门技术,应用过的应该绝大多数都会说好;没用过的举荐尝试一下,跑个 Demo 体验体验,有可能它就是你须要学习和把握的最初一门新技术了。回过头来,Flutter 到底有什么独特的魅力让它能从一众技术中怀才不遇呢?总结一下,次要有以下几点:
- 跨平台:能够做到一套代码完满适配 Android、iOS 平台,将来还会笼罩更多平台,大大节俭了开发人力与保护老本,同时领有杰出的跨端 UI 体现一致性。
- 高效开发:SDK 提供了丰盛的 UI 组件,开箱即用;申明式的 UI 构建形式,大大减少出错率;Debug 模式提供热重载能力,可实时预览代码变更,不须要从新编译装置。
- 高性能:采纳自建渲染引擎,独立于零碎并可独自优化;区别于 RN、WEEX,没有中间层转换的额定开销;Release 模式下代码编译为 AOT 指令,运行高效。
受害于以上的外围劣势,Flutter 推出后圈了很多挪动开发者的粉,各互联网大厂也纷纷将其作为一项根底技术进行钻研。在 Flutter 初期,其利用场景次要是从 0 构建一个全新 App,对混合开发的反对很不敌对。但作为一门跨平台的技术框架,到底还是须要依赖原生平台提供的诸多零碎能力,此外还有泛滥现存原生 App 蠢蠢欲动,因而在这个需要背景下,混合开发的反对与欠缺至今已倒退得越来越好,上面咱们就用一个简略的示例开始 Android 端的 Flutter 混合开发与构建之旅。
引入 Flutter 模块
要在一个已有的 Android Project 中应用 Flutter,须要引入一个 Flutter Module。在 Android Studio(须要确保 Flutter 插件曾经胜利装置并启用)中关上现有 Android 工程,通过应用 File > New > New Module… 菜单,咱们能够新创建一个 Flutter 模块或是导入一个内部的 Flutter 模块。
这里以最简略的 Android App 我的项目为例,导入 Flutter 模块。在 Flutter 模块导入胜利之后,原工程文件、构造都会产生一些变动,次要有:
- settings.gradle 文件新增了以下内容。其实就是执行对应 Flutter 模块下 .android/include_flutter.groovy 脚本文件,该步骤会引入一个名为 Flutter 的 Android Library Module,同时还会引入 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 Module,不会 apply Android 相干的任何插件,次要是蕴含 Flutter 相干源码、资源、依赖等。
- flutter:为 Flutter Gradle 插件引入的 Android Library Module;次要负责编译 flutter_module 及其依赖的第三方 Package、Plugin 的 Dart 代码,以及打包 Flutter 资源等。
- device_info:为 Flutter Gradle 插件主动引入的 Flutter Android Plugin Library Module,这是因为一开始我在 flutter_module 的 pubspec.yaml 文件中增加了对 device_info 这个插件的依赖。Flutter Gradle 工具会将 flutter_module 依赖到的所有插件其 Android 平台侧的代码、资源作为一个 Library Module 引入到我的项目中一起参加构建。如果要查看 flutter_module 引入了哪些 Plugin,能够查看其对应目录下的 .flutter-plugins 与 .flutter-plugins-dependencies 文件,这两个文件是执行 flutter pub get 时生成的,记录了插件的本地文件目录、依赖信息等。
留神:一个工程不能蕴含多个 Flutter Module,最多只能引入一个,这是由 Flutter 的 Gradle 插件决定的。
应用 Flutter
实现 Flutter 模块的引入后,咱们再来看看如何应用 Flutter。
增加依赖
首先须要在 App 模块的 build.gradle 脚本文件中增加对 Flutter 工程的依赖,只有这样 Flutter 模块才会参加到整个利用的构建中来,咱们也才可能在 App 模块中调用到 Flutter 提供的 Java 层 API。如下所示:
dependencies {implementation project(':flutter')
}
运行 Flutter 页面
咱们能够抉择应用 Activity、Fragment 或者 View 来承载 Flutter 的 UI,这里次要介绍后面两种形式,并假如 flutter_module 中曾经通过 runApp 办法渲染了一个 widget。
- 运行 Flutter Activity。应用 io.flutter.embedding.android.FlutterActivity 类能够很不便的启动一个 Flutter Activity,当然咱们也能够继承它并扩大本人的逻辑。示例代码如下:
FlutterActivity
.withNewEngine()
.build(context)
.also {startActivity(it) }
- 运行 Flutter Fragment。能够应用 FlutterFragmentActivity 或者 FlutterFragment 来增加 Flutter UI 片段:a. 应用 FlutterFragmentActivity 能够主动创立并增加一个 FlutterFragment;b. 手动创立 FlutterFragment 后增加到指标 Activity 中。示例代码如下:
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()
- 平台层和 Flutter 层通信。不论是开发 Plugin 还是业务逻辑,平台层与 Flutter 层通信是必不可少的,为此就须要应用到 MethodChannel。平台层通过 MethodChannel 申请调用 Flutter 层 API 时,数据在通过打包编码后,通过 JNI、DartVM 传到 Flutter 层解码后应用;待后果计算实现后,又会从新打包编码,通过 DartVM、JNI 传回到 Native 层;同理,在 Flutter 层申请调用平台层的 API 时,数据处理是统一的,只是流转方向相同。通过这种形式,平台层与 Flutter 层就建设了一个双向的、异步的通信通道。在上面的示例代码中,Native 层应用 dev.flutter.example/counter 创立一个 MethodChannel,并设置 Handler 接管 Dart 的近程办法调用 incrementCounter,并调用 reportCounter 将后果回传。
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:网易云信官网。
更多技术干货,欢送关注【网易智企技术 +】微信公众号。