Flutter笔记-Flutter端与Native端通信
1. 使用场景
当逻辑在Flutter中的实现方式较为困难,或者已经有现成的java代码实现了该逻辑的时候,则可以通过Flutter调用java代码来减少开发工作量,Flutter与Android提供了一个名为Channel的接口去实现通信。
2. 相关知识
2.1 Channe整体架构图
2.2 Channel分类
-
EventChannel:Flutter和平台端进行事件监听、取消等可以使用。
虽然每个Channel的功能不同但他们却都拥有三个重要的成员变量:
-
Channelname: String类型,为Channel的唯一标识符
-
messager :BinaryMessager类型,是消息信使,是消息的发送和接收工具
-
codec:MessageCodec类型或者MethodCodec类型,消息编解码器
2.3 Channelname
一个应用中会存在多个Channel,每个Channel都拥有独一无二的Channelname。每一个Channel都会存在一个对应的handler,他们的channelname相同。
2.4 BinaryMessager
Native端和Flutter端通信的核心能力来自于BinaryMessager工具,它的通信数据格式为二进制数据。当初始化一个Channel,并向这个Channel注册消息的handler的时候,其实是会生成一个BinaryMessageHandler,并以channelname为KEY注册到BinaryMessager中。
对BinaryMessager来说并不知道Channel的存在,当BinaryMessager收到消息时,会根据其传入的Channelname找到对应的handler去处理
2.5 codec
在Flutter中,定义了两种codec,一种是MessageCodec,另外一种是MethodCodec.
2.5.1 MessageCodec
用于二进制数据与普通数据之间的编解码。
在安卓中MessageCodec接口拥有两个方法:
安卓中MessageCodec接口拥有若干种实现:
- BinaryCodec,输入输出都是二进制数据,实际上就是将传入的数据原封不动的返回,可用于传递内存数据块时在编解码阶段免于拷贝
- StringCodec,用于在二进制和字符串之间进行编解码,编码格式为UTF-8
- JSONMessageCodec,用于基础数据与二进制数据之间的编解码,支持基础数据类型及列表和字典
- StandardMessageCodec,其支持基础数据类型、二进制数据、列表、字典
2.5.2 MethodMessageCodec
用于二进制数据与方法调用及方法返回结果之间的编解码操作。主要对methodcallback进行编解码
MethodMessageCodec有两个成员变量:
methodcall有两种实现:
- JSONMethodCOdec
- StandardMethodCodec(常用)
2.6 handler
每一个handler都与与channel对应,在向Channel注册一个Handler的时候,实际上是向BinaryMessager注册一个BinaryMessageHandler。
与Channel相同,Handler也分为三种:
- MessageHandler:处理字符串或者半结构化的信息,它的onMessage方法传入一个T类型的消息,并异步返回一个相同类型的result
- MethodHandler:处理方法的调用,它的onMessage方法传入一个methodcall类型的消息,并根据methodcall的method变量去执行消息对应的API,并返回执行的结果
- StreamHandler:用于事件流的通信,当实现一个StreamHandler的时候,需要实现onListen方法和onCancel方法
3.消息编解码过程
在Android,IOS,Flutter中,都有各自定义好的数据类型。当我们在Flutter端调用Android端的方法时,返回的是Java中的数据类型,例如Integer。但我们仍然可以在Flutter端使用Dart语言中的int类型去接收这个方法的返回值。在Flutter与IOS通信中也是一样的过程。这就涉及到了对消息的编解码。
Flutter官方文档表示,StandarPlantformChannel使用了StandarMessageCodec对message和response进行序列化与反序列化。message和response可以使用的类型包括boolean,numbers,Strings,bytebuffer,list,map等等
在传输过程中,所有的数据会先被序列化成二进制数据,在到达对应的平台时,会被解析成对应的数据类型。
当数据被编码的时候,会调用StandarMessageCodec的writevalue方法,这个方法接收一个名为value的参数。
在进行编码时,会根据value的类型将其type值写入二进制容器中,占1个byte,然后再将该数据转化为二进制数据写入二进制容器中。(即二进制容器中存在type+value 的二进制编码)
在进行解码时,会先从二进制容器中取出一个byte的的数据标识它的type,然后再根据type将容器中的值转化为平台对应类型的数据。
4.消息的传递过程(以methodchannel使用过程为例)
Dart层:
在Flutter端调用invokeMethod方法时,会将传入的message和argument封装成一个methodcall对象,然后使用Methodcodec将methodcall编码成二进制数据,然后通过BinaryMessager将其发出
这个方法最终会执行到ui.window 的 _sendplatformMessage方法,这个方法是一个native方法,最终实现是在native层,与JNI技术类似。
最终向native层发送三个数据:
- name:String类型,代表channel的name
- data:bytebuffer类型,代表之前封装的二进制数据
- callback:Function类型,用于方法执行结果的回调
Native层:
window.cc方法对接收到的三个参数进行处理,然后会逐层向下传递,最终会到达PlatformView类的handlerplatformMessage方法。handlePlatformMessage在不通的平台有不同的实现
PlatformViewAndroid是PlatformView在安卓端的实现。当PlatformVIewAndroid接收到PlatformMessage类型的消息时,如果消息中有response类型,则会生成一个自增长的response_id,并以response_id为KEY,response为VALUE存入字典 padding_response_ 中,然后将channel ,data转换为java可以识别的数据,通过JNI向JAVA层发起调用,将response_id,channel和data传递过去
在Java层中,别调用的是FlutterNativieView的handlePlatformMessage方法,该方法会根据传入的channel的找到对应的BinaryMessagerHandler去进行处理.
当处理结束后,FlutterNativieView会调用方法将response_id和response_data传递到native层。
在native层中,PlatformViewAndroid的InvokePlatformMessageResponseCallback会接收到response_id和response_data。首先将response_data转换为二进制数据。
并根据response_id从pending_response_字典中取出PlatformMessageResponseDart对象,并调用其complet方法将二进制结果返回。
这个二进制结果会先从native的二进制类型数据转换为dart二进制类型数据response,并调用Dart的callback将结果传递到dart层。
Dart层接收到二进制数据后,会使用MethodCodec进行解码,然后返回给业务层。
5. MethodChannel使用步骤
步骤1: 在Flutter中定义通道Channel
static const platformname= const MethodChannel($Channelname)
步骤2:在java代码中定义Channel并注册handler
MethodChannel类的构造函数的声明如下:
public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)
此处的name就是Channelname,而codec代表了消息编解码器,有默认实现。处理逻辑:
new MethodChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), $Channelname)
.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (call.method.equals(methodname)) {
...
}else{
...
}
}
});
Future<Null> methodname(param1) async {
try {
result = await platformname.invokeMethod(methodname, param1);
//result为java代码的返回结果
} on PlatformException catch (e) {
//错误回调
}
setState(() {
//修改UI
_frame = result;
});
}
6.注意:
Native端和Flutter端中的通道名称Channelname和方法名称methodname要一致
Native端的handler运行在主线程,因此不可执行耗时操作
Channel线程不安全,要注意在Native端向Flutter端返回数据的时候要在主线程
参考:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。