天天看点

Flutter端与Native端通信Flutter笔记-Flutter端与Native端通信

Flutter笔记-Flutter端与Native端通信

1. 使用场景

当逻辑在flutter中的实现方式较为困难,或者已经有现成的java代码实现了该逻辑的时候,则可以通过flutter调用java代码来减少开发工作量,Flutter与Android提供了一个名为Channel的接口去实现通信。

2. 相关知识

2.1 Channe整体架构图

Flutter端与Native端通信Flutter笔记-Flutter端与Native端通信

2.2 Channel分类

  • BasicMessageChannel:flutter和平台端进行消息数据交换时候,可以使用。
  • MethodChannel:flutter和平台端进行直接方法调用时候可以使用。
  • 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接口拥有两个方法:

  • encodeMessage方法,用于将特定数据类型T转换为二进制数据bytebuffer
  • decodeMessage方法,用于将二进制数据bytebuffer转换为特定数据类型T

安卓中MessageCodec接口拥有若干种实现:

  • BinaryCodec,输入输出都是二进制数据,实际上就是将传入的数据原封不动的返回,可用于传递内存数据块时在编解码阶段免于拷贝
  • StringCodec,用于在二进制和字符串之间进行编解码,编码格式为UTF-8
  • JSONMessageCodec,用于基础数据与二进制数据之间的编解码,支持基础数据类型及列表和字典
  • StandardMessageCodec,其支持基础数据类型、二进制数据、列表、字典

2.5.2 MethodMessageCodec

用于二进制数据与方法调用及方法返回结果之间的编解码操作。主要对methodcallback进行编解码

MethodMessageCodec有两个成员变量:

  • method:表示调用方法明后才能
  • argument:表示方法入参

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{
							...
							}
						}
					});
           

步骤3:在flutter中调用java方法并处理返回结果:

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端返回数据的时候要在主线程

参考:

深入理解Flutter Platform Channel

Flutter之MethodChannel