flutter提供了三个channel来支持dart与原生平台的交互,channel的通信方式类似rcp调用,不同的是flutter的内部实现是通过内存拷贝的方式将原生字节流转换成dart字节流。
channel是无状态通信,一次send/reply后调用就结束了,类似http的无状态通信
BinaryMessenger是flutter框架给我们提供的唯一一个用于从dart到原生消息转换的工具,所有的channel都是基于BinaryMessenger进行二次包装的,具体可以看一下BinaryMessenger提供的api。
首先,定义个MyChannel类来包裹BinaryMessenger,实现一个channel客户端(flutter端)
class MyChannel {
// channel客户端与服务端链接需要一个标识
final String name;
// channel要求通信的数据类型是ByteData
// 所以这里需要一个解码器将消息序列化/反序列化
final MessageCodec codec;
// 最终消息会通过该属性发送出去
final BinaryMessenger binaryMessenger;
// 我们直接使用Flutter提供的唯一一个BinaryMessenger,也就是defaultBinaryMessenger实例
const MyChannel(this.name, this.codec,
{this.binaryMessenger = defaultBinaryMessenger});
Future<String> send(String arg) async {
// 先将消息序列化
var data=codec.encodeMessage(arg);
var result =
//发送消息
await defaultBinaryMessenger.send(name, data);
// 接收返回值并反序列化
return codec.decodeMessage(result);
}
}
然后,定义服务端(android端)
// 定义个channel服务端与,客户端一样,都是需要发送器,解码器,以及唯一通信标识
class MyChannel(
private val binaryMessenger: BinaryMessenger,
private val name:String,
private val messageCodec: StringCodec= StringCodec.INSTANCE) {
// 与客户端不同的是服务端仅用于接收消息,所以我们要定义个消息处理类
fun setHandler(binaryMessageHandler: MyBinaryMessageHandler) {
binaryMessenger.setMessageHandler(name, binaryMessageHandler);
}
}
// 消息处理类,收到消息后将本调用,消息处理完成后,调用reply返回响应结果
class MyBinaryMessageHandler(private val messageCodec: StringCodec) : BinaryMessenger.BinaryMessageHandler {
override fun onMessage(arg: ByteBuffer?, reply: BinaryMessenger.BinaryReply) {
val argStr=messageCodec.decodeMessage(arg)
println(argStr)
reply.reply(messageCodec.encodeMessage(argStr))
}
}
最后,来看一下客户端与服务的实现
flutter
// 定义一个标识为mychannel的channel
var _channel=MyChannel("mychannel",StringCodec());
// 发送消息
var result=await _channel.send("hello");
print(result)
android
// 在onCreate方法中创建channel监听标识为mychannel的消息
override fun onCreate(savedInstanceState: Bundle?) {
...
// 获取BinaryMessenger
val binaryMessage=registrarFor("package top.guodf.channel_example").messenger()
val channel=MyChannel(binaryMessage,"mychannel")
// 创建一个MyBinaryMessageHandler用来对接收到消息进行处理
channel.setHandler(MyBinaryMessageHandler(StringCodec.INSTANCE))
}
至此,一个简单的自定义channel就实现了,这个例子包含了所有channel通信的原理,这是一个从flutter到android的实现,channel同样支持从android到flutter的通信,只要将上面的客户端与服务端代码反过来实现就行了,下面我们实现类似EventChannel的流实现。
前面我们说了channel是一次行通信,那么怎么实现持续响应呢?这里我参考了EventChannel的实现,下面做一个简化版本的demo
flutter端实现
//在MyChannel中添加如下方法
Stream<String> eventStream(String msg) {
//定义一个Stream,供flutter端持续接收服务端的消息
var controller = StreamController<String>.broadcast();
//这里是重点,创建一个服务端,供android调用,这个方法让flutter也变成了服务端
defaultBinaryMessenger.setMessageHandler(name, (data) {
if (data == null) controller.close();
var value = codec.decodeMessage(data);
//收到服务端消息时写入controller,监听controller.stream的都能收到通知
controller.add(value);
return Future.value(data);
});
Future(() async {
//这里是重点,因为channel都是一次性通信,所以我们持续的通知android端我们在等待消息
//从这里可以看到流实际是在flutter控制的
await for (var _ in controller.stream) {
send(msg);
}
});
//第一次由flutter端发起调用激活事件流
send(msg);
return controller.stream;
}
android端实现
//定义一个新得handler专门用于处理事件流
class MyEventMessageHandler(private val channel:MyChannel, private val messageCodec: StringCodec):BinaryMessenger.BinaryMessageHandler {
@SuppressLint("SimpleDateFormat")
override fun onMessage(arg: ByteBuffer?, reply: BinaryMessenger.BinaryReply) {
val formatter = SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss")
val curDate = Date(System.currentTimeMillis())
val str = formatter.format(curDate)
//调用flutter端(因为flutter我们已经再监听了,所以可以收到消息)
channel.send(str);
//一次调用结束,通知flutter端
reply.reply(messageCodec.encodeMessage(null))
}
}
下面的实现虽然也可以让flutter端持续收到消息,但是无法更新widget*(还没有理解为什么 )*
flutter端
//在MyChannel中添加如下方法
Stream<String> eventStream(String msg) {
//定义一个Stream,供flutter端持续接收服务端的消息
var controller = StreamController<String>.broadcast();
//这里是重点,创建一个服务端,供android调用,这个方法让flutter也变成了服务端
defaultBinaryMessenger.setMessageHandler(name, (data) {
if (data == null) controller.close();
var value = codec.decodeMessage(data);
//收到服务端消息时写入controller,监听controller.stream的都能收到通知
controller.add(value);
return Future.value(data);
});
//第一次由flutter端发起调用激活持续流
send(msg);
return controller.stream;
}
android端的错误实现
//定义一个新得handler专门用于处理事件流
class MyEventMessageHandler(private val channel:MyChannel, private val messageCodec: StringCodec):BinaryMessenger.BinaryMessageHandler {
@SuppressLint("SimpleDateFormat")
override fun onMessage(arg: ByteBuffer?, reply: BinaryMessenger.BinaryReply) {
//为了保证flutter端可以收到通知,我们将通知放在最前面
reply.reply(messageCodec.encodeMessage(null))
//这种方式其实也可以持续发送消息到flutter端,
//但是会导致如果flutter端无法更新widget
while(true){
val formatter = SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss")
val curDate = Date(System.currentTimeMillis())
val str = formatter.format(curDate)
//调用flutter端(因为flutter我们已经再监听了,所以可以收到消息)
channel.send(str);
}
}
}
上面的文章已经说明了channel的同时原理及实现,至于c++部分是怎么讲dart字节与原生字节转换的我解答不了,已经超出了我的认知返回。
如果你想让MyChannel支持多种类型,可以像MethodChannel一样将MyChannel定义为泛型的版本:MyChannel<T>
。
个人建议还是不要自定义channel,flutter提供的三种已经完全可以满足需求了。
代码在这里:https://github.com/guodf/study_flutter/tree/master/channel