通信在任何程序中都是举足轻重的,在做Flutter开发的时候通常离不了Flutter和Native之间的通信,比如:初始化Flutter时Native向Dart传递数据,Dart调用Native的相册选择图片,Dart调用Native的模块进行一些复杂的计算,Native将一些数据(GPS信息,陀螺仪,传感器等)主动传递给Dart等。当然,这种通信的感觉有点像原生app调用C/C++,不用担心这里不需要写jni,不需要会cmake,但是这里需要懂原生app基本开发了,同理ios也是一样的。
言归正传,这里将向大家介绍在Flutter中Dart和Native之间通信的几种方式以及其原理和使用技巧;接下来我将分场景来介绍Dart 和Native之间的通信。
几种通信场景:
1、初始化Flutter时Native向Dart传递数据;
2、Native发送数据给Dart;
3、Dart发送数据给Native;
4、Dart发送数据给Native,然后Native回传数据给Dart;
Flutter 与 Native通信机制
在讲解Flutter 与 Native之间是如何传递数据之前,我们先来了解下 Flutter与Native的通信机制,Flutter和Native的通信是通过Channel来完成的。
消息使用Channel(平台通道)在客户端(UI)和主机(平台)之间传递,如下图所示:
Flutter中消息的传递是完全异步的;
Channel所支持的数据类型对照表:
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
Flutter定义了三种不同类型的Channel:
BasicMessageChannel:用于传递字符串和半结构化的信息,持续通信,收到消息后可以回复此次消息,如:Native将遍历到的文件信息陆续传递到Dart,在比如:Flutter将从服务端陆陆续获取到信息交个Native加工,Native处理完返回等;
MethodChannel:用于传递方法调用(method invocation)一次性通信:如Flutter调用Native拍照;
EventChannel: 用于数据流(event streams)的通信,持续通信,收到消息后无法回复此次消息,通常用于Native向Dart的通信,如:手机电量变化,网络连接变化,陀螺仪,传感器等;
这三种类型的类型的Channel都是全双工通信,即A <=> B,Dart可以主动发送消息给platform端,并且platform接收到消息后可以做出回应,同样,platform端可以主动发送消息给Dart端,dart端接收数后返回给platform端。
BasicMessageChannel用法
Native端:
构造方法
BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec)
-
BinaryMessenger messenger
- 消息信使,是消息的发送与接收的工具; -
String name
- Channel的名字,也是其唯一标识符; -
MessageCodec<T> codec
- 消息的编解码器,它有几种不同类型的实现: -
BinaryCodec
- 最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已。或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝; -
StringCodec
- 用于字符串与二进制数据之间的编解码,其编码格式为UTF-8; -
JSONMessageCodec
- 用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。其在iOS端使用了NSJSONSerialization作为序列化的工具,而在Android端则使用了其自定义的JSONUtil与StringCodec作为序列化工具; -
StandardMessageCodec
- 是BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典,其工作原理;
setMessageHandler方法
void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler)
-
BasicMessageChannel.MessageHandler<T> handler
- 消息处理器,配合BinaryMessenger
完成消息的处理;
在创建好BasicMessageChannel
后,如果要让其接收Dart发来的消息,则需要调用它的setMessageHandler
方法为其设置一个消息处理器。
BasicMessageChannel.MessageHandler原型
public interface MessageHandler<T> {
void onMessage(T var1, BasicMessageChannel.Reply<T> var2);
}
-
onMessage(T var1, BasicMessageChannel.Reply<T> var2)
- 用于接受消息,var1是消息内容,var2是回复此消息的回调函数;
send方法原型
void send(T message)
void send(T message, BasicMessageChannel.Reply<T> callback)
-
T message
- 要传递给Dart的具体信息; -
BasicMessageChannel.Reply<T> callback
- 消息发出去后,收到Dart的回复的回调函数;
在创建好BasicMessageChannel
后,如果要向Dart发送消息,可以调用它的send
方法向Dart传递数据。
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String>, BasicMessageChannel.Reply<String> {
private final Activity activity;
private final BasicMessageChannel<String> messageChannel;
static BasicMessageChannelPlugin registerWith(FlutterView flutterView) {
return new BasicMessageChannelPlugin(flutterView);
}
private BasicMessageChannelPlugin(FlutterView flutterView) {
this.activity = (Activity) flutterView.getContext();
this.messageChannel = new BasicMessageChannel<>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
//设置消息处理器,处理来自Dart的消息
messageChannel.setMessageHandler(this);
}
@Override
public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//处理Dart发来的消息
reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
if (activity instanceof IShowMessage) {
((IShowMessage) activity).onShowMessage(s);
}
Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
}
/**
* 向Dart发送消息,并接受Dart的反馈
*
* @param message 要给Dart发送的消息内容
* @param callback 来自Dart的反馈
*/
void send(String message, BasicMessageChannel.Reply<String> callback) {
messageChannel.send(message, callback);
}
@Override
public void reply(String s) {
}
}
Dart端:
构造方法原型
const BasicMessageChannel(this.name, this.codec);
-
String name
- Channel的名字,要和Native端保持一致; -
MessageCodec<T> codec
- 消息的编解码器,要和Native端保持一致,有四种类型的实现具体可以参考Native端的介绍;
setMessageHandler方法原型
void setMessageHandler(Future<T> handler(T message))
-
Future<T> handler(T message)
- 消息处理器,配合BinaryMessenger
完成消息的处理;
在创建好BasicMessageChannel
后,如果要让其接收Native发来的消息,则需要调用它的setMessageHandler
方法为其设置一个消息处理器。
send方法原型
Future<T> send(T message)
-
T message
- 要传递给Native的具体信息; -
Future<T>
- 消息发出去后,收到Native回复的回调函数;
在创建好BasicMessageChannel
后,如果要向Native发送消息,可以调用它的send
方法向Native传递数据。
import 'package:flutter/services.dart';
static const BasicMessageChannel _basicMessageChannel =
const BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());
//使用BasicMessageChannel接受来自Native的消息,并向Native回复
_basicMessageChannel
.setMessageHandler((String message) => Future<String>(() {
setState(() {
showMessage = message;
});
return "收到Native的消息:" + message;
}));
//使用BasicMessageChannel向Native发送消息,并接受Native的回复
String response;
try {
response = await _basicMessageChannel.send(value);
} on PlatformException catch (e) {
print(e);
}