前言
在上篇中,我和大家分享了我对于Android
原生与Flutter
模块混合构建的一些经验,在本篇中,我将和大家分享Android
原生和Flutter
模块通信方面的经验。
所谓通信
事先声明,我这里所说的通信,指的就是Android
原生和Flutter
模块之间的方法互调,数据传递。 Flutter
官方提供了Channel
也即平台通道
帮助开发者实现:
-
Flutter
向原生
平台发送消息。 -
原生
平台监听通道并接收消息。然后,处理逻辑并将数据整合好之后作为响应发送回Flutter
。
Flutter
官方提供了三种Channel
用于跨平台通信
Channel | 功能 |
---|---|
MethodChannel | 跨平台方法互调 |
EventChannel | 跨平台消息传递 |
BasicMessageChannel | 跨平台元数据传递 |
你以为我要开始 1-2-3 开始给你介绍这三种Channel
了吗?不!
官方文档在编写自定义的特定平台代码一节中,以获取原生平台电池电量为例演示了如何使用MethodChannel
进行跨平台的方法调用,不难发现在MethodChannel
的使用中,有两处(每个平台)使用了硬编码字符串用于各平台间的标识、匹配。这还只是调用了一个原生平台的方法,实际生产中十个,百个方法都是有可能的!
鄙人十分讨厌这种分散的无法管理的用于标识、匹配的硬编码字符串,因为团队中英文水平参差不齐,即便都很好,谁能保证自己不会手抖拼错呢,这就增加了一个可能的出错点。因此,官网也在后半篇介绍了一种更安全的跨平台通信的方式。
Pigeon
Pigeon
是一个用于Flutter
和原生
平台之间建立类型安全的通信的代码生成工具,更快更易用。
Pigeon
免去了在Flutter
和原生
之间用硬编码字符串的方式匹配通道、消息的名称和数据类型,并要求Flutter
和原生
声明相同的参数和数据类型,生成以结构化类型安全方式发送消息的代码。
其实,观察Pigeon
生成的代码可以发现,Pigeon
其实也是借助BasicMessageChannel
实现通信的,只不过它以脚本的方式生成了模板代码,正是在这个过程中避免了手写硬编码可能出现的问题。
Pigeon
的使用
添加Pigeon
依赖
dart pub add pigeon
然后
dart pub get
创建pigeon
文件夹并新建一个platformApi.dart
文件用于声明对应的方法
这里
platformApi.dart
的文件命名可自定义
pigeon/platformApi.dart
import 'package:pigeon/pigeon.dart';
// 这是声明 Flutter 调用原生方法
// 如果是原生调用 Flutter 方法,请使用 @FlutterApi() 注解
@HostApi()
abstract class AndroidBatteryApi {
int getAndroidBattery();
}
新建Pigeon
脚本用于生成对应的通信代码
复制下方代码创建一个run_pigeon.sh
文件,将下方代码复制,针对你的项目修改成对应的文件名,文件路径
$ flutter pub run pigeon \
--input pigeon/platformApi.dart \
--dart_out lib/api_generated.dart \
--experimental_kotlin_out android/app/src/main/kotlin/com/randalldev/native_provider/Pigeon.kt \
--experimental_kotlin_package "com.randalldev.native_provider"
我这里只生成了Android
平台的模板代码
pigeon
工具还有其他命令选项,比如iOS
平台
--objc_header_out ios/Runner/pigeon.h \
--objc_source_out ios/Runner/pigeon.m \
--experimental_swift_out ios/Runner/Pigeon.swift \
执行脚本,会生成各端需要的原生代码
Windows
编译环境,AS
的Terminal
工具内直接输入下方命令
run_pigeon.sh
MacOS
或Liunx
编译环境,AS
的Terminal
工具内需要输入下方命令
./run_pigeon.sh
实现原生方法的逻辑
pigeon
脚本生成代码是接口类,我们需要实现接口,并实现其内部的方法
打开android/app/src/main/kotlin/com/randalldev/native_provider/MainActivity.kt
使用延迟加载的方式新建一个实例,这是一种更安全更方便的写法,根据需要的参数选择内部类的方式有时也可以,关注内部实现即可
private val batteryApi by lazy {
object : AndroidBatteryApi {
override fun getAndroidBatteryApi(): Long {
TODO("Not yet implemented")
}
}
}
TODO部分就是我们需要实现的Android
平台的逻辑
针对需要异步操作才能获取返回值的情况
这种情况通常出现在一些网络操作、文件操作的情况。
声明异步的方法
还是之前的platformApi.dart
文件,增加一个异步方法的声明
···
@async
int getAsyncAndroidBattery();
···
运行run_pigeon.sh
脚本,生成新声明的方法,并在Android
平台添加实现
private val batteryApi by lazy {
···
override fun getAsyncAndroidBattery(callback: (Long) -> Unit) {
TODO("Not yet implemented")
}
···
}
实现异步逻辑
private val batteryApi by lazy {
···
override fun getAsyncAndroidBattery(callback: (Long) -> Unit) {
// 演示代码,实际没有这样的 api
system.getBattery(object : BatteryResultListener {
override fun onSucceed(battery: Integer) {
callback.invoke(battery)
}
override fun onFailed(msg: String?) {
callback.invoke(-1)
}
})
}
···
}
总结
Flutter
提供了Channel
用于实现与原生平台的通信,但是基于我个人的技术选择,我选择了使用官方提供的代码生成工具Pigeon
去生成Channel
Api 的代码。
使用Pigeon
主要是以下这几个步骤
- 添加依赖
- 创建
pigeon
相关dart
文件并声明需要的方法 - 编写并运行
pigeon
脚本 - 在
原生
平台实现Pigeon
生成的抽象方法,并返回对应的数据
参考文章
Writing custom platform-specific code
作者:Randall_Xia
链接:https://juejin.cn/post/7192589048727207973
网友评论