赞
踩
上一篇文章中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<T> { private static final String TAG = "BasicMessageChannel#"; private final BinaryMessenger messenger; private final String name; private final MessageCodec<T> 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<T> {
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个用途侧重点不一样,其实原理都是一样的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。