Flutter圈子Flutter中文社区Flutter

Flutter之旅:平台通道(Platform Channel)

2019-03-18  本文已影响22人  风少侠

作为一个UI框架,Flutter提供了三种通道来和原生平台通信。

image.png

三种方式的设计是非常相似的,分别维护了两个成员属性:

另外,三种方式其实都是通过BinaryMessages来进行消息的传递,它负责将二进制消息发送到平台插件并从平台插件接收二进制消息。最后通过消息编解码器来将二进制信息转换成我们需要的数据类型,注意三种通信方式都是双向的。以BasicMessageChannel为例:

  Future<T> send(T message) async {
    return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
  }

细心的同学会发现,图上还有一个OptionalMethodChannel,这并不是一种新的通信方式,而是MethodChannel的进一步封装,如果找不到对应插件,返回的是null,而不再抛出MissingPluginException异常。

class OptionalMethodChannel extends MethodChannel{
  ...
  @override
  Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
    try {
      final T result = await super.invokeMethod<T>(method, arguments);
      return result;
    } on MissingPluginException {
      return null;
    }
  } 
  ...
}

BasicMessageChannel

使用介绍

BasicMessageChannel_Flutter.png BasicMessageChannel_Android.png

三种通道分别在flutter和其他平台都提供了相关实现,并提供了相似的api,这里以BasicMessageChannel为例展示一下。

成员属性

Api接口

发送消息

flutter端:可以看到发送消息是个异步方法,大概执行顺序就是先将数据编码成字节数据,通过BinaryMessages传输,等待native返回数据,再解码成我们需要的数据,如果native没有返数据,则Future为null。

  Future<T> send(T message) async {
    return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
  }

android端:其实实现是类似的,只不过不是使用Future,而是用回调的方式来监听返回数据。

    public void send(T message) {
        this.send(message, (BasicMessageChannel.Reply)null);
    }

    public void send(T message, BasicMessageChannel.Reply<T> callback) {
        this.messenger.send(this.name, this.codec.encodeMessage(message), callback == null ? null : new BasicMessageChannel.IncomingReplyHandler(callback));
    }

接收消息

flutter端:我们可以看到传入的参数是一个异步函数,handler接收的参数为native平台发送的数据,handler的返回值将作为响应返回给native端。

  void setMessageHandler(Future<T> handler(T message)) {
    if (handler == null) {
      BinaryMessages.setMessageHandler(name, null);
    } else {
      BinaryMessages.setMessageHandler(name, (ByteData message) async {
        return codec.encodeMessage(await handler(codec.decodeMessage(message)));
      });
    }
  }

android端:同样是通过接口回调的方式来接收和响应消息的传递。

    public void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler) {
        this.messenger.setMessageHandler(this.name, handler == null ? null : new BasicMessageChannel.IncomingMessageHandler(handler));
    }

    public interface MessageHandler<T> {
        void onMessage(T message, BasicMessageChannel.Reply<T> reply);
    }

示例代码

我们来做这样一个demo,flutter发送一条消息到native,native收到消息后给个回复,并发送一条新的消息到flutter,flutter收到后再回复给native。
flutter:

PluginChannel.listenBasicMessage();
RaisedButton(
            onPressed: () {
              PluginChannel.sendBasicMessage();
            },
            child: Text("BasicMessageChannel"),
          )
...
class PluginChannel {
  static const _basicMessageChannelName = "study_3/basicMessageChannel";
  static const _basicMessageChannel = BasicMessageChannel(_basicMessageChannelName, StandardMessageCodec());

  static void listenBasicMessage(){
    _basicMessageChannel
        .setMessageHandler((result) async{
      print('flutter listen:$result');
      return "flutter response to native";
    });
  }
  static void sendBasicMessage() {
    _basicMessageChannel
        .send("flutter send to native")
        .then((result) {
      print('flutter receive response:$result');
    });
  }
}

android:

    private val BASIC_MESSAGE_CHANNEL = "study_3/basicMessageChannel"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GeneratedPluginRegistrant.registerWith(this)

        basicMessageChanelDemo()
    }
    private fun basicMessageChanelDemo(){
        BasicMessageChannel(this.flutterView,BASIC_MESSAGE_CHANNEL, StandardMessageCodec.INSTANCE)
                .setMessageHandler { any, reply ->
                    println("android listen:$any")
                    reply.reply("android response to flutter")


                    BasicMessageChannel(this.flutterView,BASIC_MESSAGE_CHANNEL, StandardMessageCodec.INSTANCE)
                            .send("android send to flutter"){
                                println("android receive response:$it")
                            }
                }
    }

日志:

I/System.out(27557): android listen:flutter send to native
I/flutter (27557): flutter receive response:android response to flutter
I/flutter (27557): flutter listen:android send to flutter
I/System.out(27557): android receive response:flutter response to native

MethodChannel

一些想法

MethodChannel通过传递方法名和参数,来达到通信的效果,给我的感觉和BasicMessageChannel并没有本质上的不同,使用BasicMessageChannel传递一个数据,做一系列操作再返回数据,给我的感觉效果是一样的。为了验证自己的想法,大概翻了一下源码:
首先看两者的发送数据方法:

  ///BasicMessageChannel
  Future<T> send(T message) async {
    return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
  }

  ///MethodChannel
  Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
    assert(method != null);
    final ByteData result = await BinaryMessages.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    final T typedResult = codec.decodeEnvelope(result);
    return typedResult;
  }

两者内部的实现逻辑基本上是一样的,编码成二进制、BinaryMessages发送、解码。都是调用的BinaryMessages的send方法,唯一区别就是编解码器codec的不同。下面来看看codec的实现:

  ///BasicMessageChannel    StandardMessageCodec
  ByteData encodeMessage(dynamic message) {
    if (message == null)
      return null;
    final WriteBuffer buffer = WriteBuffer();
    writeValue(buffer, message);
    return buffer.done();
  }

  ///MethodChannel     StandardMethodCodec
  const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]);

  ByteData encodeMethodCall(MethodCall call) {
    final WriteBuffer buffer = WriteBuffer();
    messageCodec.writeValue(buffer, call.method);
    messageCodec.writeValue(buffer, call.arguments);
    return buffer.done();
  }

我们可以看到StandardMethodCodec中的messageCodec默认为StandardMessageCodec,而且编码方法和StandardMessageCodec的编码方法是一样的。默认实现就是相当于把方法名和参数封装成了一个MethodCall对象,再通过BasicMessageChannel传递。

Api

//发送
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {}
//接收
void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {}

Api不再赘述,设计和使用都类似于BasicMessageChannel,发送的时候传入方法名和参数,返回一个Future来监听响应。接收的时候传入一个高阶函数,参数为收到的信息,返回值为要响应的数据。

示例代码

依然是一个flutter和android相互调用的demo。
flutter:

PluginChannel.listenMethod();

onPressed: () {
   PluginChannel.sendBasicMessage();
},

class PluginChannel {
  static const _methodChannelName = "study_3/methodChannel";
  static const _methodChannel = MethodChannel(_methodChannelName);

  static void invokeMethod() {
    _methodChannel.invokeMethod("getAge", {"name": "lili"}).then((result) {
      print('flutter receive response:$result');
    });
  }

  static void listenMethod() {
    _methodChannel.setMethodCallHandler((methodCall) async {
      print('flutter listen:$methodCall');
      return "男";
    });
  }
}

android:

private val METHOD_CHANNEL = "study_3/methodChannel"
override fun onCreate(savedInstanceState: Bundle?) {
    methodChannelDemo()
}


    private fun methodChannelDemo(){
        MethodChannel(this.flutterView,METHOD_CHANNEL)
                .setMethodCallHandler { methodCall, result ->
                    println("android listen:${methodCall.method} \t ${methodCall.arguments}")
                    when(methodCall.method){
                        "getAge" -> {
                            result.success(getAge(methodCall.argument<String>("name")))
                        }
                    }


                    MethodChannel(this.flutterView,METHOD_CHANNEL)
                            .invokeMethod("getSex", mapOf(Pair("name","tom")), object : MethodChannel.Result {
                                override fun notImplemented() {
                                    println("android receive notImplemented")
                                }

                                override fun error(p0: String?, p1: String?, p2: Any?) {
                                    println("android receive error")
                                }

                                override fun success(p0: Any?) {
                                    println("android receive response:$p0")
                                }
                            })
                }
    }

    private fun getAge(name:String?): Int{
        return when(name){
            "lili" -> 18
            "tom" -> 19
            "allen" -> 20
            else -> 0
        }
    }

日志:

I/System.out( 9700): android listen:getAge {name=lili}
I/flutter ( 9700): flutter receive response:18
I/flutter ( 9700): flutter listen:MethodCall(getSex, {name: tom})
I/System.out( 9700): android receive response:男

EventChannel

Api

EventChannel并没有分别提供发送和收听消息的方法,它只提供了一个receiveBroadcastStream方法,用于发送消息,同时返回一个流(Stream),用于监听平台插件成功返回的所有事件信息,这个流可以被监听不止一次。因此我们可以用于native端需要持续传递数据到flutter的情况,比如监听电量,调用摄像头等等。

  Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);
    StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen, onCancel);
    return controller.stream;
  }

上面是receiveBroadcastStream的抽象代码,总共做了两件事:

      BinaryMessages.setMessageHandler(name, (ByteData reply) async {
        if (reply == null) {
          controller.close();
        } else {
          try {
            controller.add(codec.decodeEnvelope(reply));
          } on PlatformException catch (e) {
            controller.addError(e);
          }
        }
        return null;
      });
      try {
        await methodChannel.invokeMethod<void>('listen', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'while activating platform stream on channel $name',
        ));
      }

可以看到onListen中依然是做了两件事情:

      BinaryMessages.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod<void>('cancel', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'while de-activating platform stream on channel $name',
        ));
      }

依然两件事:

可以想象,native平台肯定也定义了一个MethodChannel,用来接收listen和cancel方法,我们来验证一下,以Android端为例,EventChannel部分源码:

       public void onMessage(ByteBuffer message, BinaryReply reply) {
            MethodCall call = EventChannel.this.codec.decodeMethodCall(message);
            if (call.method.equals("listen")) {
                this.onListen(call.arguments, reply);
            } else if (call.method.equals("cancel")) {
                this.onCancel(call.arguments, reply);
            } else {
                reply.reply((ByteBuffer)null);
            }
        }

我们可以看到,当收到消息的时候,有三种情况:

示例代码

flutter:

class PluginChannel {

  static const _eventChannelName = "study_3/eventChannel";
  static const _eventChannel = EventChannel(_eventChannelName);

  static void event() {
    _eventChannel.receiveBroadcastStream("event arg")
        .listen((result) {
      print('flutter listen:$result');
    });
  }
}

android:

    private fun eventChannelDemo(){
        EventChannel(this.flutterView,EVENT_CHANNEL)
                .setStreamHandler(object : EventChannel.StreamHandler {
                    override fun onListen(p0: Any?, events: EventChannel.EventSink?) {
                        println("android onListen:$p0")

                        events?.success(1)
                        events?.success(2)
                        events?.success(3)
                        events?.success(4)
                        events?.endOfStream()
                        events?.success(5)
                    }

                    override fun onCancel(p0: Any?) {
                        println("android onCancel:$p0")
                    }
                })
    }

日志:

I/System.out( 9271): android onListen:event arg
I/flutter ( 9271): flutter listen:1
I/flutter ( 9271): flutter listen:2
I/flutter ( 9271): flutter listen:3
I/flutter ( 9271): flutter listen:4
I/System.out( 9271): android onCancel:event arg

完整代码地址

上一篇下一篇

猜你喜欢

热点阅读