Flutter 使用了一套灵活的系统来调用平台特定的APIs,无论是iOS还是安卓。
Flutter支持平台特定API不是依赖于生成代码的方式,而是通过灵活的消息传递。
- 应用的Flutter部分发送消息到它的宿主(应用的iOS或者Android部分)通过,platform channel.
- 应用宿主在platform channel上面监听并且接受消息。然后调用任意数量的特定平台的API(通过原生语言),然后发送一个回调给到Flutter 部分。
注意 如果您需要使用Java/Kotlin/Objective-C或Swift中的平台api或库,本文将介绍如何使用平台通道机制。但是,通过检查defaultTargetPlatform属性,您还可以在Flutter应用程序中编写特定于平台的Dart代码。平台适配列出了一些特定于平台的适配,这些适配会在框架中自动执行Flutter。
平台渠道的架构概述
使用平台通道在客户端(UI)和主机(平台)之间传递消息,如下图所示:
消息和响应是异步传递的,以确保用户界面保持响应性。
尽管Flutter是异步地向Dart发送消息,但无论何时调用channel方法,都必须在平台的主线程上调用该方法。
在客户端,MethodChannel (API)允许发送与方法调用对应的消息。在平台端,Android上的MethodChannel (API)和iOS上的FlutterMethodChannel (API)支持接收方法调用和发送结果。这些类允许您用很少的“样板”代码开发平台插件。
平台通道数据类型支持和编解码器
标准平台通道使用标准消息编解码器,该编解码器支持对简单的json类值(如布尔值、数字、字符串、字节缓冲区以及这些值的列表和映射)的高效二进制序列化(有关详细信息,请参阅StandardMessageCodec)。当您发送和接收值时,这些值与消息之间的序列化和反序列化将自动发生。
下表显示了在平台端接收Dart值的方式,反之亦然。
好了,原理介绍完了,下面一起来看一下具体是如何跑起来的。参照官方文档做了一个在iOS平台获取手机电量的例子。这个例子会示范如何调用平台特定的API来检索以及展示当前设备的电量。它将会使用Android BatteryManager API,和iOS device.batteryLevel API,通过一个单独的平台方法,getBatteryLevel()。
创建一个flutter工程
使用如下命令创建:1
flutter create platform_channel
或者你可以通过设置特定的语言来指定默认的模板:1
flutter create -i objc -a java platform_channel
使用你安装的编译器来打开创建好的工程,我使用的是idea,然后在你工程中的main.dart里面添加如下代码:
1 | class _MyHomePageState extends State<MyHomePage> { |
简单解释一下:首先创建了platform 实例,这个就是通信的通道。
client 和 host之间通过通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的,给通道名字添加域前缀,例如:charles.flutter.dev/battery。
_getBatteryLevel()方法就是异步的获取当前设备电量的方法。这个方法中最核心的一句:
1 | final int result = await platform.invokeMethod('getBatteryLevel'); |
就是通过通道,去调用host端的getBatteryLevel方法,并且把获取到的值但会到client端。
展示电量的部分就不介绍了。大家可以看下文附的Demo.
host端的话,只介绍iOS端了,安卓类似。有兴趣的朋友可以自己看看。
从flutter 项目目录下找到 ios/Runner.xcworkspace 双击打开。
在Appdelegate 里面添加如下代码:
1 |
|
解释一下:这段代码也是先创建一个通道 batteryChannel,可以看到这里使用的channel name 和上文中提到的 client 端里面的完全一致。同时设置了消息的发送者。
getBatteryLevel 就是iOS获取电量的方法,不做过多解释,到此为止,你可以运行platform_channel flutter项目来看一下是否可以获取到当前手机的电量。
效果如下:
以上介绍的就是利用MethodChannel
从Flutter端调用平台端函数的方法。接下来我们介绍一下如何从平台端调用Flutter端的函数,我们需要在平台端代码部分编写如下方法:
1 | - (void)invokeFlutterMethod:(FlutterMethodChannel *)channel { |
在调用返回电量结果后,调用该方法,为了效果明显我们延迟3秒钟调用:
1 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
而在Flutter端需要编写如下代码,在build
方法中添加监听:
1 | _platform.setMethodCallHandler(platformCallHandler); |
实现platformCallHandler
和flutterMethod
方法:
1 | Future<dynamic> platformCallHandler(MethodCall call) async { |
然后再次运行程序,当获取完电量后3秒,按钮下方的文字会变成”已经获取到电量了!!!”(此处就不再提供Gif了)
附:本文的Demo