
Flutter 混合开发(三):Android 与 Flutter 之间通信详细指南
我们在做 Flutter 混合开发的时候通常需要进行 Flutter 和 Native 之间的通信。 比如 Dart 调用 Native 的相册选择图片,Native 将电量、GPS 信息主动传递给 Dart 等等。在混合开发中通信通常有以下几种:
第一种通信方式我们在讲解原生项目接入 Flutter 时已经讲解过,有兴趣的同学可以移步到Flutter 混合开发(一):Android 项目集成 Flutter 模块详细指南看下。
Flutter 与 Native 端之间的通信机制是通过 Platform Channel 来完成。消息使用 Channel 在 Flutter 端和 Native 端进行传递。具体如下图所示:
从图中可以看出,两端之间的通信都是双向的,而且是完成异步传递。Flutter 定义了三种不同类型的 Channel:
下面我们就来看看这三种 Channel 通信方式的具体使用和介绍。
BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec) 当我们需要接受来自 Dart 端发送的消息时使用 setMessageHandler 方法:
void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler) 参数 handler 是消息处理器,配合 BinaryMessenger 来完成对消息的处理。它是一个接口,具体的实现在 onMessage 方法中:
public interface MessageHandler<T> { void onMessage(T message, BasicMessageChannel.Reply<T> reply); } 参数 message 即为 Dart 发送的数据, reply 是用于回复消息的回调函数,提供 reply.reply("")设置回复的内容。
上面讲的是接受 Dart 端的消息,那么 Native 端主动发送消息则是使用 send 方法,它有两个重载方法:
void send(T message) void send(T message, BasicMessageChannel.Reply<T> callback) 参数 message 即为要发生给 Dart 的数据,callback 回调则是用于接收 Dart 端收到消息后的回复信息。
const BasicMessageChannel(this.name, this.codec); 这里的 name 和 codec 和 Android 端的构造方法参数是一样的,那么是 channel 的名字也是唯一标识,codec 是消息编解码器,两个参数在两端必须统一。
Dart 端如果要接受 Native 端的消息则要设置 setMessageHandler 方法:
void setMessageHandler(Future<T> handler(T message)) 参数 handler 为消息处理器,配合 BinaryMessenger 来完成对消息的处理。
通过 send 方法向 Native 端发送消息:
Future<T> send(T message) 参数 message 为要传递的参数。Future<t>为发送消息后等待 Native 回复的回调函数。</t>
//初始化 BasicMessageChannel BasicMessageChannel<String> basicMessageChannel = new BasicMessageChannel<>(flutterView, "BasicMessageChannelPlugin",StringCodec.INSTANCE); //接受消息 basicMessageChannel.setMessageHandler((message, reply) -> { mTvDart.setText(message); reply.reply("收到 dart 数据:接受成功"); }); //发送消息 basicMessageChannel.send(message, reply -> mTvDart.setText(reply)); //初始化 BasicMessageChannel static const BasicMessageChannel<String> _basicMessageChannel = BasicMessageChannel("BasicMessageChannelPlugin", StringCodec()); // 接受消息 void handleBasicMessageChannel() { _basicMessageChannel .setMessageHandler((String message) => Future<String>(() { setState(() { showMessage = message; }); return "收到 Native 的消息:接受成功"; })); } //发送消息 respOnse= await _basicMessageChannel.send(_controller.text); setState(() { showMessage = response; }); 最后效果为下图,红色分割线上部分为 Native 页面,下部分为 Flutter 页面。
使用 MethodChannel 相关方法的参数类型及含义和 BasicMessageChannel 的参数含义都是相同的,下面就不一一解释了。
MethodChannel(BinaryMessenger messenger, String name) MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) 第一个构造函数会构造一个 StandardMethodCodec.INSTANCE 类型的 MethodCodec。MethodCodec 定义了两种类型:JSONMethodCodec 和 StandardMethodCodec。
如果想接受来自 Dart 端的消息则使用:
setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) MethodCallHandler 为接口,回调方法为:
public interface MethodCallHandler { void onMethodCall(MethodCall call, MethodChannel.Result result); } call 参数有两个成员变量,String 类型的 call.method 表示调用的方法名,Object 类型的 call.arguments 表示调用方法所传递的入参。result 是回复此消息的回调函数提供了 result.success,result.error,result.notImplemented 方法调用。
发送消息主动调用 Dart 代码则使用 invokeMethod 方法
invokeMethod(@NonNull String method, @Nullable Object arguments) invokeMethod(String method, @Nullable Object arguments, Result callback) 第二个方法多了一个 callback,它是用来接受 Dart 端收到消息后的回复信息。
public interface Result { void success(@Nullable Object result); void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails); void notImplemented(); } const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]) 构造函数默认是使用 StandardMethodCodec 编解码器。
通过 setMethodCallHandler 方法接受来自 Native 的方法调用,通过 invokeMethod 方法调用 Native 端的方法。
void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) //初始化 MethodChannel MethodChannel methodChannel = new MethodChannel(flutterView, "MethodChannelPlugin"); mBtnTitle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //调用 dart 端 getPlatform 方法 methodChannel.invokeMethod("getPlatform", null, new MethodChannel.Result() { @Override public void success(@Nullable Object result) { mTvDart.setText(result.toString()); } @Override public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) { mTvDart.setText(errorCode + "==" + errorMessage); } @Override public void notImplemented() { mTvDart.setText("未实现 getPlatform 方法"); } }); } }); //接受 dart 的调用 methodChannel.setMethodCallHandler((call, result) -> { switch (call.method) { case "getBatteryLevel": int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success("电量为:" + batteryLevel); } else { result.error("1001", "调用错误", null); } break; default: result.notImplemented(); break; } }); // receive void handleMethodChannelReceive() { Future<dynamic> platformCallHandler(MethodCall call) async { switch (call.method) { case "getPlatform": return getPlatformName(); //调用 success 方法 // return PlatformException(code: '1002',message: "出现异常"); //调用 error break; } } _methodChannel.setMethodCallHandler(platformCallHandler); // _methodChannel.setMethodCallHandler(null); //调用 notImplemented } //send void handleMethodChannelSend() async { try { respOnse= await _methodChannel.invokeMethod("getBatteryLevel"); print(response); setState(() { showMessage = response; }); } catch (e) { //捕获 error 和 notImplemented 异常 setState(() { showMessage = e.message; }); } } 当我们在使用 setMethodCallHandler 接受到 native 的消息时,直接调用相关方法即可调用 Native 端的 success 回调。
如果直接抛异常如 PlatformException,那么就调用 Native 端的 error 回调。
PlatformException(code: '1002',message: "出现异常") 如果我们直接设置 handler 为 null
_methodChannel.setMethodCallHandler(null); 那么就会调用 Native 端的 notImplemented 方法回调。
同理我们在 Dart 端使用 invokeMethod 方法是,需要进行异常捕获以便于我们接受到 Native 端调用的 error 和 notImplemented 方法回调。
最后效果为下图,红色分割线上部分为 Native 页面,下部分为 Flutter 页面。
EventChannel 内部实现原理其实也是通过 MethodChannel 来完成的。
EventChannel(BinaryMessenger messenger, String name) EventChannel(BinaryMessenger messenger, String name, MethodCodec codec) 同样的,也是两个构造,默认 codec 为 StandardMethodCodec,EventChannel 和 MethodChannel 的 codec 都属于 MethodCodec 范畴。
通过 setStreamHandler 来监听 Dart 端发送的消息,
void setStreamHandler(EventChannel.StreamHandler handler) 其中 handler 是一个接口:
public interface StreamHandler { void onListen(Object args, EventChannel.EventSink eventSink); void onCancel(Object o); } args 为 dart 端初始化监听流的参数,eventSink 设置了三个回调,分别是 success、error 和 endofStream。分别对应 Dart 端的 ondata、error 和 onDone 回调。
const EventChannel(this.name, [this.codec = const StandardMethodCodec()]); 通过 EventChannel 初始化一个 channel 对象。如果从 Native 中接受数据需要定义一个广播流:
Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) 通过调用 Stream 的 listen 方法来完成注册。
EventChannel eventChannel = new EventChannel(flutterView, "EventChannelPlugin"); eventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { events.success(arguments.toString() + getBatteryLevel()); //events.error("111","出现错误",""); //events.endOfStream(); } @Override public void onCancel(Object arguments) { } }); //init static const EventChannel _eventChannel = EventChannel("EventChannelPlugin"); //receive void handleEventChannelReceive() { streamSubscription = _eventChannel .receiveBroadcastStream() //可以携带参数 .listen(_onData, onError: _onError, onDone: _onDone); } void _onDone() { setState(() { showMessage = "endOfStream"; }); } _onError(error) { setState(() { PlatformException platformException = error; showMessage = platformException.message; }); } void _onData(message) { setState(() { showMessage = message; }); } 通过 event.success 方法发送信息,dart 端通过监听 Stream 流来获取信息。当 Native 端调用 events.error 时在 Dart 端的 onError 回调中需要将 error 转换为 PlatformException 才能获取到异常的相关信息。
最后效果为下图,红色分割线上部分为 Native 页面,下部分为 Flutter 页面。
主要是讲解了 Android 端和 Dart 的三种通信方式。详细分析了方法构成和具体的实例使用。每一种方式都对应不同的使用场景,大家可以按需选择,多加练习做到熟能生巧。
文中都是贴的一些代码片段,全部 Demo 源码已经上传到后台,关注公众号回复「混合开发」即可获取下载链接。
Flutter 开发必备 Dart 基础:Dart 快速入门