天天看点

Flutter Chanel通信流程

01.flutter和原生之间交互

02.MethodChanel流程

03.MethodChanel使用流程

04.MethodChanel代码实践

05.EventChannel流程

06.EventChannel基本流程

07.EventChannel代码实现

08.BasicMessageChannel流程

09.BasicMessageChannel基本流程

10.BasicMessageChannel代码实现

11.Channel编解码器说明

12.Channel通信可以子线程吗

13.Channel通信传递稳定性

14.onActivityResult如何实现

官方给的通信方式

看图片,channel通信方式

从底层来看,Flutter和平台端通信的方式是发送异步的二进制消息,该基础通信方式在Flutter端由BinaryMessages来实现, 而在Android端是一个接口BinaryMessenger,其具体实现为FlutterNativeView,在iOS端是一个协议 FlutterBinaryMessenger,FlutterViewController遵守并实现了这个协议。

flutter可以与native之间进行通信,帮助我们使用native提供的能力。

通信是双向的,我们可以从Native层调用flutter层的dart代码,同时也可以从flutter层调用Native的代码。

我们需要使用Platform Channels APIs进行通信,主要包括下面三种:

MethodChannel:用于传递方法调用(method invocation)

EventChannel:用于事件流的发送(event streams)

BasicMessageChannel:用于传递字符串和半结构化的消息,这里什么叫做半结构化?下面会解释……

channel通信是异步还是同步的

为了保证用户界面在交互过程中的流畅性,无论是从Flutter向Native端发送消息,还是Native向Flutter发送消息都是以异步的形式进行传递的。那为何不使用同步来操作,下面会说到……

几种channel应用场景分析

MethodChannel使用场景:无论是Flutter端还是Native端都可以通过MethodChannel向对方平台发送两端提前定义好的方法名来调用对方平台相对应的消息处理逻辑并且带回返回值给被调用方。

EventChannel的使用场景:更侧重于Native平台主动向Flutter平台,单向给Flutter平台发送消息,Flutter无法返回任何数据给Native端,EventChannel描述是单通的。可以类比Android里面的广播……

BasicMessageChannel的使用场景:比如flutter想拍照,拍完照后的图片路径需要传给flutter,照片的路径发送可以使用BasicMessageChannel.Reply回复,也可以使用sendMessage主动再发一次消息。个人认为接收消息并回复消息属于一次通信,所以倾向于使用BasicMessageChannel.Reply。

混合开发通常用那种channel

只是混合开发通常涉及到两端频繁通信,个人更加倾向使用BasicMessageChannel,不分主客,使用和通信更方便。

MethodCall

方法调用Java层封装,主要是数据类

MethodChannel

这个主要用户和dart进行方法通信,类

MethodCallHandler

这个java层处理dart层时间的接口,在通讯协议中属于上层接口,接口

BinaryMessageHandler

java层和dart层通讯的最底层抽象接口,面向二进制数据包,接口

DartMessenger

最底层用于接收JNI发送过来的数据。实现类

DartExecutor

配置、引导并开始执行Dart代码。BinaryMessenger的具体实现类

FlutterView

NA用来承载flutter的容器view

IncomingMethodCallHandler

BinaryMessageHandler的实现类,用户接收底层发送过来的数据包,然后转发给MethodCallHandler,并对MethodCallHandler 发送过的结果进行打包发送给dart层。实现类

FlutterJNI

JNI层的封装用于跟底层引擎侧进行通讯

其中最常用的是MethodChanel,MethodChanel的使用与在Android的JNI调用非常类似,但是MethodChanel更加简单,而且相对于JNI的同步调用MethodChanel的调用是异步的:

从flutter架构图上可以看到,flutter与native的通信发生在Framework和Engine之间,framewrok内部会将MethodChannel以BinaryMessage的形式与Engine进行数据交换。

flutter调用native步骤

[native] 使用MethodChannel#setMethodCallHandler注册回调

[flutter] 通过MethodChannel#invokeMethod发起异步调用

[native] 调用native方法通过Result#success返回Result,出错时返回error

[flutter] 收到native返回的Result

如图所示

native调用flutter

与flutter调用native的顺序完全一致,只是[native]与[flutter]角色反调

NA端使用MethodChannel

首先定义Channel名称,需要保证是唯一的,在Flutter端需要使用同样的名称来创建MethodChannel。如果名称不一样,则会导致匹配不上……

第一个参数:是messenger,类型是BinaryMessenger,是一个接口,代表消息信使,是消息发送与接收的工具;

第二个参数:是name,就是Channel名称,和flutter定义的要一样;

第三个参数:是codec,类型是MethodCodec,代表消息的编解码器,如果没有传该参数,默认使用StandardMethodCodec。

定义好了MethodChannel之后调用setMethodCallHandler()方法设置消息处理回调,参数是MethodHandler类型,需要实现它的onMethodCall()方法。onMethodCall()方法有两个参数methodCall和result,methodCall记录了调用的方法信息,包括方法名和参数,result用于方法的返回值,可以通过result.success()方法返回信息给Flutter端。

可以通过invokeMethod方法让NA执行调用flutter方法。那么执行了flutter方法后需要回传数据,这个时候就需要用到Result接口呢,代码如下所示:

事件接收处理端

接收处理回调时onMethodCall(MethodCall call, MethodChannel.Result result)通过methodCall接收事件发送者传递回来的信息,通过Result把处理完的结果发送给事件发送方。

通过methodCall.method:来区分不同函数名(方法)名以执行不同的业务逻辑,

通过methodCall.hasArgument("key"):判断是否有某个key对应的value

通过methodCall.argument("key"):获取key对应的value值

通过result.success(object):把处理完的结果返回给事件发送方

事件发送端

处理事件发送方通过methodChannel.invokeMethod("方法名","要传递的参数")把需要传递的参数传递给事件监听者。 其中

方法名:不能为空

要传递的参数:可以为空,若不为空则必须为可Json序列化的对象。

callback:可以为空,若不为空则表示执行了flutter方法后的回调监听状态

Flutter使用MethodChannel

在Flutter端同样需要定义一个MethodChannel,使用MethodChannel需要引入services.dart包,Channel名称要和Android端定义的相同。

添加监听NA调用flutter方法的监听,flutter代码是setMethodCallHandler方法实现。return则表示flutter回传给NA的数据操作。

flutter是如何给NA发送消息的呢,直接调用invokeMethod方法,代码如下所示

EventChannel用于从native向flutter发送通知事件,例如flutter通过其监听Android的重力感应变化等。与MethodChannel不同,EventChannel是native到flutter的单向调用,调用是多播(一对多)的,可以类比成Android的Brodecast广播。

照例先看一下API使用的基本流程:

[native]EventChannel#setStreamHandler注册Handler实现

[native]EventChannel初始化结束后,在StreamHandler#onLister回调中获取EventSink引用并保存

[flutter]EventChannel#receiveBroadcastStream注册listener,建立监听

[native]使用EventSink#sucess发送通知事件

[flutter]接受到事件通知

[native]通知结束时调用endOfStream结束

flutter端

创建EventChannel,注册“包名/标识符”的channel名

通过StreamSubscription#listen注册listener,其中cancelOnError参数表示遇到错误时是否自动结束监听

native(android)端

通过EventChannel#setStreamHandler注册Handler实现

初始化完成后,获取eventSink引用并保存

eventSink发送事件通知

通知结束时调用event#endOfStream,此时onCancel会被调用

必要时,可通过evnetSink#error发送错误通知,flutter的StreamSubscription#onError会收到通知

BasicMessageChannel用于在flutter和native互相发送消息,一方给另一方发送消息,收到消息之后给出回复。

flutter向native发送消息

[flutter]创建BasicMessageChannel

[native]通过BasicMessageChannel#MessageHandler注册Handler

[flutter]通过BasicMessageChannel#send发送消息

[native]BasicMessageChannel#MessageHandler#onMessage中接收消息,然后reply

native向flutter发送消息

流程也是一样的,只是将[flutter]与[native]反调

flutter需要完成以下工作

创建BasicMessageChannel

通过BasicMessageChannel#send发送消息

相对与其他Channel类型的创建,MessageChannel的创建除了channel名以外,还需要指定编码方式:

发送的消息会以二进制的形式进行处理,所以要针对不同类型的数进行二进制编码

编码类型 消息格式

BinaryCodec 发送二进制消息时

JSONMessageCodec 发送Json格式消息时

StandardMessageCodec 发送基本型数据时

StringCodec 发送String类型消息时

代码

android端完成以下工作:

通过setHandler注册MessageHandler

MessageHandler#onMessage回调中接收到message后,通过reply进行回复

什么是消息编解码器

在Flutter和平台间进行相互通信了,但是收发的数据都是二进制的,这就需要开发者考虑更多的细节,如字节顺序(大小端)和怎么表示更高级的消息类型,如字符串,map等。

因此,Flutter 还提供了消息编解码器(Codec), 用于高级数据类型(字符串,map等)和二进制数据(byte)之间的转换,即消息的序列化和反序列化。

消息编解码器种类有哪些

MethodCodec:方法传递的编解码器抽象,接口

JSONMethodCodec:MethodCodec的实现类,会把数据打包成json结构发送给dart,类

StandardMethodCodec:MethodCodec的实现类,会把数据打包成默认格式发送给dart,类

BinaryCodec

MessageCodec的实现类,直接发送二进制数据

BinaryCodec是最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已。或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝。

StringCodec

MessageCodec的实现类,负责解码和编码String类型的消息

使用 UTF-8 编码格式对字符串数据进行编解码,在Android平台转换为 java.util.String 类型

JSONMessageCodec

MessageCodec的实现类,负责解码和编码Json类型的消息

JSONMessageCodec用于处理 JSON 数据类型(字符串型,数字型,布尔型,null,只包含这些类型的数组,和key为string类型,value为这些类型的map),在编码过程中,数据会被转换为JSON字符串,然后在使用 UTF-8 格式转换为字节型。

StandardMessageCodec

MessageCodec的实现类,负责解码和编码默认类型的消息

StandardMessageCodec 可以认为是 JSONMessageCodec 的升级版,能够处理的数据类型要比 JSONMessageCodec 更普遍一些,且在处理 int 型数据时,会根据 int 数据的大小来转为平台端的32位类型(int)或者是64位类型(long),StandardMessageCodec 也是 Flutter Platform channel 的默认编解码器

首先看下MessageCodec

StandardMessageCodec稍微复杂

StandardMessageCodec在写入数据的时候,显示写入这个数据的类型值定义,然后在写入其对应的具体值,什么意思呢?

查看一下如何写入指定类型的值,代码如下所示:

查看一下如何读取指定类型的值,代码如下所示:

编解码的实现类并不复杂

可以先了解一下这个比较能更好的理解数据传递,其实不关java上层使用那种方式,最终传递给底层数据都是固定格式,约定统一的数据格式双方才能识别出来,正常的来说用默认的编解码格式就可以了。

关于四种解码器使用场景

暂未找到使用的场景

适用发送单一的字符串数据,数据量单一的情况,比如LifecycleChannel

适用数据量比较复杂的情况,比如有携带多个数据字段的传递,比如KeyEventChannel

默认的数据编解码,绝大多数的情况下使用默认的就可以了。比如:MethodChannel,EventChannel

首先看一下Android发送通信信息,主要分析入口是:nativeChannel.invokeMethod("setNum", a , null);

最终定位找到DartMessenger类的send方法,代码如下所示:

尝试一下子线程发送消息,发现会出现崩溃

崩溃信息如下所示

从method.invokeMethod('android', map);开始分析

最后定位到_DefaultBinaryMessenger类中的send方法

channel传递数据是否会丢失,如何测试呢?可以模拟,Android给flutter发送1000条信息,然后flutter给Android发送1000条信息,接下来看一下如何测试:

Android给flutter发送数据,代码如下所示

flutter接收数据并回传数据给Android

Android发送消息日志

flutter接收Android发送数据

flutter收到消息后,回调给Android数据。Android监听回调数据,打印日志如下

然后再看一波打印日志,如下所示

因此查看日志可以得知,传递数据保证了数据的时效性,发送消息和接收消息是一一对应。并没有失败的情况,因此传递数据是稳定的。

重点说明,有小伙伴有疑惑,你这遍历1000次,每次传递都是int值,那实际开发中可能传递大json,数据量大的情况会怎样,这个下面会说到……

先说一个场景

在开发中我们经常会遇到关闭当前页面的同时返回给上一个页面数据的场景,在Android中是通过startActivityForResult和onActivityResult()实现的。

而纯Flutter页面之间可以通过在Navigator.of(context).pop()方法中添加参数来实现,那么对于Flutter页面和Android原生页面之间如何在返回上一页时传递数据呢,通过MethodChannel就可以实现。

在Flutter端调用原生的返回方法就可以了,首先在Flutter页面添加一个按钮,点击按钮返回原生页面,代码如下:

Android端依然是通过判断methodCall.method的值来执行指定的代码,通过methodCall.argument()获取Flutter传递的参数。

Android原生页面返回Flutter页面

这种情况需要原生来调用Flutter代码,和Flutter调用原生方法的步骤是一样的。首先触发flutter页面按钮,从flutter跳转na页面,然后触发na页面返回操作,返回到Flutter页面,并传递数据。

首先是flutter页面触发跳转到na页面的代码操作逻辑,代码如下所示

然后接下来的一步是,从NA返回到flutter页面,然后再去调用flutter方法。具体操作代码如下所示