Flutter学习-Channel详解

Flutter学习-Channel详解

前言

上一篇文章中Flutter学习-插件开发学习插件开发,提到了一个很重要的通信platform channel:MethodChannel。其实flutter和本地的交互,还有其他channel。为了更好的学习插件,先来了解下各个channel,包括Channel分类、如何工作(消息如何从Flutter端传递到Platform端,消息如何编解码,Platform Channel工作在什么线程上,是否线程安全,Platform Channel能否传递大内存数据块等待)以便后续使用。参考:https://segmentfault.com/a/1190000016190851

channel分类

BasicMessageChannel:用于传递字符串和半结构化的信息
MethodChannel:用于传递方法调用(上一篇已经提到)
EventChannel:用于数据流(event streams)的通信。

三种Channel之间互相独立,每种Channel均有三个重要成员变量:
• name: String类型,代表Channel的名字,也是其唯一标识符。

• messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。

• codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。

Flutter学习-Channel详解_第1张图片
定义如下:


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,定义如下Flutter学习-Channel详解_第2张图片
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");
        }
    }

handler说明

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个用途侧重点不一样,其实原理都是一样的。

你可能感兴趣的:(Flutter框架)