前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 ># flutter之channel详解

# flutter之channel详解

作者头像
用户1175783
发布2019-09-17 09:57:27
2.7K2
发布2019-09-17 09:57:27
举报

flutter之channel详解

代码语言:javascript
复制
flutter提供了三个channel来支持dart与原生平台的交互,channel的通信方式类似rcp调用,不同的是flutter的内部实现是通过内存拷贝的方式将原生字节流转换成dart字节流。
  • MethodChannel 通过定义对应的资源名称实现与平台进行一次性通信。
  • EventChannel 通过流的方式,持续接收对方的通信数据,内部包装的MethodChannel。
  • BasicMessageChannel 与MethodChannel类似,不同的是需要指定一个解码器,这个channel与MethodChannel没有本质区别。

交互原理

channel是无状态通信,一次send/reply后调用就结束了,类似http的无状态通信

channel核心之BinaryMessenger

BinaryMessenger是flutter框架给我们提供的唯一一个用于从dart到原生消息转换的工具,所有的channel都是基于BinaryMessenger进行二次包装的,具体可以看一下BinaryMessenger提供的api。

下面我们自定义一个MyChannel来实现dart到原生的通信:

首先,定义个MyChannel类来包裹BinaryMessenger,实现一个channel客户端(flutter端)

代码语言:javascript
复制
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端)

代码语言:javascript
复制
// 定义个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

代码语言:javascript
复制
// 定义一个标识为mychannel的channel
var _channel=MyChannel("mychannel",StringCodec());
// 发送消息
var result=await _channel.send("hello");
print(result)

android

代码语言:javascript
复制
// 在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的流实现。

MyEventChannel接收服务端的持续响应

前面我们说了channel是一次行通信,那么怎么实现持续响应呢?这里我参考了EventChannel的实现,下面做一个简化版本的demo

flutter端实现

代码语言:javascript
复制
//在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端实现

代码语言:javascript
复制
//定义一个新得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端

代码语言:javascript
复制
//在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端的错误实现

代码语言:javascript
复制
//定义一个新得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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • flutter之channel详解
  • 交互原理
    • channel核心之BinaryMessenger
      • MyEventChannel接收服务端的持续响应
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档