上一篇文章中Flutter学习-插件开发学习插件开发,提到了一个很重要的通信platform channel:MethodChannel。其实flutter和本地的交互,还有其他channel。为了更好的学习插件,先来了解下各个channel,包括Channel分类、如何工作(消息如何从Flutter端传递到Platform端,消息如何编解码,Platform Channel工作在什么线程上,是否线程安全,Platform Channel能否传递大内存数据块等待)以便后续使用。参考:https://segmentfault.com/a/1190000016190851
BasicMessageChannel:用于传递字符串和半结构化的信息
MethodChannel:用于传递方法调用(上一篇已经提到)
EventChannel:用于数据流(event streams)的通信。
三种Channel之间互相独立,每种Channel均有三个重要成员变量:
• name: String类型,代表Channel的名字,也是其唯一标识符。
• messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。
• codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。
public final class BasicMessageChannel {
private static final String TAG = "BasicMessageChannel#";
private final BinaryMessenger messenger;
private final String name;
private final MessageCodec codec;
}
public final class MethodChannel {
private static final String TAG = "MethodChannel#";
private final BinaryMessenger messenger;
private final String name;
private final MethodCodec codec;
}
public final class EventChannel {
private static final String TAG = "EventChannel#";
private final BinaryMessenger messenger;
private final String name;
private final MethodCodec codec;
}
name说明
一个Flutter应用中可能存在多个Channel,每个Channel在创建时必须指定一个独一无二的name,Channel之间使用name来区分彼此。当有消息从Flutter端发送到Platform端时,会根据其传递过来的channel name找到该Channel对应的Handler(消息处理器)。
消息信使:BinaryMessenger说明
虽然三种Channel各有用途,但是他们与Flutter通信的工具却是相同的,均为BinaryMessager。
BinaryMessager是一个接口, Binarymessenger在Android端是一个接口,其具体实现为FlutterNativeView,定义如下
FlutterNativeView中实现send等方法,其最终会调用nativeDispatchPlatformMessage方法(本地方法)进行消息发送,其中ByteBuffer message为byte即二进制数据,就是说,通信使用的消息格式为二进制格式数据。
而BinaryReply 为响应数据,也是二进制数据。
//FlutterNativeView.class:
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if(!this.isAttached()) {
Log.d("FlutterNativeView", "FlutterView.send called on a detached view, channel=" + channel);
} else {
int replyId = 0;
if(callback != null) {
replyId = this.mNextReplyId++;
this.mPendingReplies.put(Integer.valueOf(replyId), callback);
}
if(message == null) {
nativeDispatchEmptyPlatformMessage(this.mNativePlatformView, channel, replyId);
} else {
nativeDispatchPlatformMessage(this.mNativePlatformView, channel, message, message.position(), replyId);
}
}
}
BinaryMessenger在用户测使用时候从哪里获取呢?在创建activity时候,继承FlutterActivity后,进行插件注册,此时通过
FlutterActivity的registrarFor()获取PluginRegistry.Registrar。Registrar中可以通过messenger()获取到插件的BinaryMessenger 。
//Registrar 定义
public interface Registrar {
Activity activity();
Context context();
Context activeContext();
BinaryMessenger messenger();
}
class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
//通过PluginRegistry拿到Registrar
FlutterPluginTestPlugin.registerWith(registry.registrarFor("com.demo.flutterplugintest.FlutterPluginTestPlugin"));
}
}
public interface PluginRegistry {
PluginRegistry.Registrar registrarFor(String var1);
}
public class FlutterPluginTestPlugin implements MethodCallHandler {
public static void registerWith(Registrar registrar) {
//通过Registrar 获取到信使BinaryMessenger
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test");
channel.setMethodCallHandler(new FlutterPluginTestPlugin());
}
信使BinaryMessenger起到消息的接收发送等功能,具体实现还没有研究。、
Codec说明
消息编解码器Codec主要用于将二进制格式的数据转化为Handler能够识别的数据,Flutter定义了两种Codec:MessageCodec和MethodCodec。
MessageCodec:可以从文章开始看到,MethodCodec 适用于MessageChannel。定义如下:
public interface MessageCodec {
ByteBuffer encodeMessage(T var1);
T decodeMessage(ByteBuffer var1);
}
MessageCodec实现类有以下几个:
BinaryCodec:直接是二进制
JSONMessageCodec:Json转二进制、二进制转Json
StandardMessageCodec:对char int long double array的二进制转换
StringCodec:字符串转二进制、二进制转字符串,其编码格式为UTF-8
Flutter默认的消息编解码器是StandardMessageCodec。
由此可见,上一篇Flutter学习-插件开发提到的是否支持自定义类型,答案是可以的,需要自己实现这个codec。
也回答了上一篇的问题,问题如下:
MethodCodec:是对方法调用的编解码解析。可以从文章开始看到,MethodCodec 适用于EventChannel、MethodChannel。
public interface MethodCodec {
ByteBuffer encodeMethodCall(MethodCall var1);
MethodCall decodeMethodCall(ByteBuffer var1);
ByteBuffer encodeSuccessEnvelope(Object var1);
ByteBuffer encodeErrorEnvelope(String var1, String var2, Object var3);
Object decodeEnvelope(ByteBuffer var1);
}
实现类有以下几个:
JSONMethodCodec:通过json格式和二进制转换数据。json格式为{“method”:method,“args”:args}。
编解码格式:
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
JSONObject map = new JSONObject();
map.put("method", methodCall.method);
map.put("args", JSONUtil.wrap(methodCall.arguments));
.....
}
public MethodCall decodeMethodCall(ByteBuffer message) {
try {
Object json = JSONMessageCodec.INSTANCE.decodeMessage(message);
if(json instanceof JSONObject) {
JSONObject map = (JSONObject)json;
Object method = map.get("method");
Object arguments = this.unwrapNull(map.opt("args"));
if(method instanceof String) {
return new MethodCall((String)method, arguments);
}
}
throw new IllegalArgumentException("Invalid method call: " + json);
} catch (JSONException var6) {
throw new IllegalArgumentException("Invalid JSON", var6);
}
}
StandardMethodCodec:标准格式是通过MethodCall 转二进制或者二进制转MethodCall来使用
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
this.messageCodec.writeValue(stream, methodCall.method);
this.messageCodec.writeValue(stream, methodCall.arguments);
ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
buffer.put(stream.buffer(), 0, stream.size());
return buffer;
}
public MethodCall decodeMethodCall(ByteBuffer methodCall) {
methodCall.order(ByteOrder.nativeOrder());
Object method = this.messageCodec.readValue(methodCall);
Object arguments = this.messageCodec.readValue(methodCall);
if(method instanceof String && !methodCall.hasRemaining()) {
return new MethodCall((String)method, arguments);
} else {
throw new IllegalArgumentException("Method call corrupted");
}
}
Flutter定义了三种类型的Handler,与Channel类型一一对应。我们向Channel注册一个Handler时,实际上就是向BinaryMessager注册一个与之对应的BinaryMessageHandler。当消息派分到BinaryMessageHandler后,Channel会通过Codec将消息解码,并传递给Handler处理。
MessageHandler
MessageHandler用户处理字符串或者半结构化的消息,其onMessage方法接收一个T类型的消息,并异步返回一个相同类型result。MessageHandler的功能比较基础,使用场景较少,但是其配合BinaryCodec使用时,能够方便传递二进制数据消息。
MethodHandler
MethodHandler用于处理方法的调用,其onMessage方法接收一个MethodCall类型消息,并根据MethodCall的成员变量method去调用对应的API,当处理完成后,根据方法调用成功或失败,返回对应的结果。
StreamHandler
StreamHandler与前两者稍显不同,用于事件流的通信,最为常见的用途就是Platform端向Flutter端发送事件消息。当我们实现一个StreamHandler时,需要实现其onListen和onCancel方法。而在onListen方法的入参中,有一个EventSink(其在Android是一个对象,iOS端则是一个block)。我们持有EventSink后,即可通过EventSink向Flutter端发送事件消息。
Platform Channel的代码运行在什么线程?
Flutter Engine自己不创建线程,其线程的创建于管理是由enbedder提供的,并且Flutter Engine要求Embedder提供四个Task Runner,分别是Platform Task Runner,UI Task Runner,GPU Task Runner和IO Task Runner。
实际上,在Platform侧执行的代码运行在Platform Task Runner中,而在Flutter app侧的代码则运行在UI Task Runner中。在Android和iOS平台上,Platform Task Runner跑在主线程上。因此,不应该在Platform端的Handler中处理耗时操作
Platform Channel是否线程安全
Platform Channel并非是线程安全的,故我们在将Platform端的消息处理结果回传到Flutter端时,需要确保回调函数是在Platform Thread(也就是Android和iOS的主线程)中执行的。
是否支持大内存数据块的传递
Platform Channel实际上是支持大内存数据块的传递,当需要传递大内存数据块时,需要使用BasicMessageChannel以及BinaryCodec。
3种channel应该怎么选择?其实根据以上的分析,已经很明确了。
BasicMessageChannel:flutter和平台端进行消息数据交换时候,可以使用。
MethodChannel:flutter和平台端进行直接方法调用时候可以使用。
EventChannel:flutter和平台端进行事件监听、取消等可以使用。
虽然3个用途侧重点不一样,其实原理都是一样的。