Flutter Channel通信原理

1,996 阅读12分钟

上篇文章, 我们一起学习了官方文档中, 是怎样使用Platform Channel跟平台通信的。这篇文章, 我们一起来探讨Platform Channel跟平台通信的原理。

Platform Channel简介

Flutter中, 常用的三种Platform Channel用来支持和平台之间数据的传递, 分别是:

  • BasicMessageChannel: 支持字符串和半结构化的数据传递。双向传递。
  • MethodChannel: 支持方法调用,既可以从Flutter发平台发起方法调用,也可以从平台代码向Flutter发起调用。 双向传递。
  • EventChannel: 支持数据流通信。

这三种Platform Channel分别有不同的作用。但在设计上大同小异,他们都有以下三个成员变量:

  • name:表示Channel名字,每个Channel使用唯一的name作为其唯一标志
  • codec: 表示消息的编解码器,目前有MethodCodec和MessageCodec两种类型
  • messager:信使,是消息的发送和接受工具

Platform Channel结构

为了对这三种Platform Channel有个比较感性的认识,通过以下简化过的代码来熟悉它们的结构.

三个Platform Channel方式源码如下, 我们一起看下。

BasicMessageChannel

class BasicMessageChannel<T> {
  /// Creates a [BasicMessageChannel] with the specified [name], [codec] and [binaryMessenger].
  ///
  /// The [name] and [codec] arguments cannot be null. The default [ServicesBinding.defaultBinaryMessenger]
  /// instance is used if [binaryMessenger] is null.
  const BasicMessageChannel(this.name, this.codec, { BinaryMessenger? binaryMessenger })
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MessageCodec<T> codec;

  /// The messenger which sends the bytes for this channel, not null.
  BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;
  final BinaryMessenger? _binaryMessenger;
 
  /// codec先将消息message编码, binaryMessenger将编码后的消息发送给iOS、Android平台,并等待iOS、Android平台的回复消息。
  Future<T?> send(T message) async {
    return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
  }

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

  // Looking for setMockMessageHandler?
  // See this shim package: packages/flutter_test/lib/src/deprecated.dart
}

MethodChannel

class MethodChannel {
  /// Creates a [MethodChannel] with the specified [name].
  ///
  /// The [codec] used will be [StandardMethodCodec], unless otherwise
  /// specified.
  ///
  /// The [name] and [codec] arguments cannot be null. The default [ServicesBinding.defaultBinaryMessenger]
  /// instance is used if [binaryMessenger] is null.
  const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger? binaryMessenger ])
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MethodCodec codec;

  BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;
  final BinaryMessenger? _binaryMessenger;

  @optionalTypeArgs
  Future<T?> _invokeMethod<T>(String method, { required bool missingOk, dynamic arguments }) async {
    assert(method != null);
    final ByteData? result = await binaryMessenger.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      if (missingOk) {
        return null;
      }
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    return codec.decodeEnvelope(result) as T?;
  }

  d call arguments on Android.
  @optionalTypeArgs
  Future<T?> invokeMethod<T>(String method, [ dynamic arguments ]) {
    return _invokeMethod<T>(method, missingOk: false, arguments: arguments);
  }

  Future<List<T>?> invokeListMethod<T>(String method, [ dynamic arguments ]) async {
    final List<dynamic>? result = await invokeMethod<List<dynamic>>(method, arguments);
    return result?.cast<T>();
  }

 
  Future<Map<K, V>?> invokeMapMethod<K, V>(String method, [ dynamic arguments ]) async{
    final Map<dynamic, dynamic>? result = await invokeMethod<Map<dynamic, dynamic>>(method, arguments);
    return result?.cast<K, V>();
  }

  void setMethodCallHandler(Future<dynamic> Function(MethodCall call)? handler) {
    binaryMessenger.setMessageHandler(
      name,
      handler == null
        ? null
        : (ByteData? message) => _handleAsMethodCall(message, handler),
    );
  }

  Future<ByteData?> _handleAsMethodCall(ByteData? message, Future<dynamic> Function(MethodCall call) handler) async {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
      return codec.encodeSuccessEnvelope(await handler(call));
    } on PlatformException catch (e) {
      return codec.encodeErrorEnvelope(
        code: e.code,
        message: e.message,
        details: e.details,
      );
    } on MissingPluginException {
      return null;
    } catch (e) {
      return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
    }
  }

  // Looking for setMockMethodCallHandler or checkMethodCallHandler?
  // See this shim package: packages/flutter_test/lib/src/deprecated.dart
}

EventChannel

class EventChannel {
  const EventChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger? binaryMessenger])
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MethodCodec codec;

  /// The messenger used by this channel to send platform messages, not null.
  BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;
  final BinaryMessenger? _binaryMessenger;

  Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);
    late StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async {
      binaryMessenger.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: ErrorDescription('while activating platform stream on channel $name'),
        ));
      }
    }, onCancel: () async {
      binaryMessenger.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod<void>('cancel', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while de-activating platform stream on channel $name'),
        ));
      }
    });
    return controller.stream;
  }
}

三种通信方式的name(Channel名称)、codec(编解码器)、messager(信使)

  • name(Channel名称): name是用于区分不同Platform Channel的唯一标志.在一个Flutter应用中,通常会存在多个Platform Channel,不同Channel之间通过name那么来区分.比如在使用MethodChannel平台发起方法调用时,需要为MethodChannel指定对应的name.
  • messager(信使): messager也称为信使,通俗来说信使就是现代的快递员,它负责把数据从Flutter搬运到JAndroid/IOS平台,或者从Android/IOS搬运到Flutter).对于Flutter中的三种Channel,尽管各自用途不同,但messager都是BinaryMessager。当我们创建一个Channel时,并为其设置消息处理器时,最终会为该Channel绑定一个BinaryMessagerHandler.并以Channel的name为key,保存在Map结构中.当接受到发送消息后,会根据消息中携带的channel名称取出对应BinaryMessagerHandler,并交由其处理.在Android平台中,BinaryMessenger是一个接口,其实现类是FlutterNativeView
  • Codec(编解码器): 在Platform Channel中,binaryMessenger携带的数据需要在Dart层,Native层以及Android/IOS平台中传输,需要考虑一种与平台无关的数据协议,且又能支持图片/文件等资源,因此官方最终采用了二进制字节流作为数据传输协议:发送方需要把数据编码成二进制数据,接受方再把数据解码成原始数据.而负责编解码操作的就是Codec。

三种通信方式的接收各个platform(iOS、Android)的消息方式有所不同, 分别是:

  • BasicMessageChannel: 使用setMessageHandler回调接收platform发过来的消息。
  • MethodChannel: 使用setMethodCallHandler回调接收platform发过来的消息。
  • EventChannel: 使用receiveBroadcastStream回调接收platform发过来的消息。

其实, 本质上, Platform Channel通信还是通过信使BinaryMessenger来完成消息的收、发操作。 默认信使是ServicesBinding.instance!.defaultBinaryMessenger

总结, 我们可以知道

  • Platform Channel通信还是通过信使BinaryMessenger来完成消息的收、发操作。
  • 默认信使是ServicesBinding.instance!.defaultBinaryMessenger
  • codec先将消息message编码, binaryMessenger将编码后的消息发送给iOS、Android平台,并等待iOS、Android平台的回复消息。

重点讲解Codec(编解码器)

Flutter中, 有两种Code

  • MethodCodec: 用于对MethodCall编解码
  • MessageCodec: 用于对Message进行编解码

MethodCodec

MethodCodec用于二进制数据与方法调用(MethodCall)和返回结果之间的编解码.MethodChannel和EventChannel所使用的编解码器均为MethodCodec.

  • 基础的MethodCodec: 基础的MethodCall, 一个MethodCall对象, 需要方法名, 以及可选的参数

abstract class MethodCodec {
  /// 将methodCall编码为二进制ByteBuffer
  ByteData encodeMethodCall(MethodCall methodCall);

  /// 将二进制methodCall解码为MethodCall
  MethodCall decodeMethodCall(ByteData? methodCall);

  /// 将二进制数据ByteBuffer解码成dynamic
  dynamic decodeEnvelope(ByteData envelope);

  /// 将正常响应结果result编码为二进制ByteData
  ByteData encodeSuccessEnvelope(Object? result);

  /// 将错误响应提示编码为二进制ByteData
  ByteData encodeErrorEnvelope({ required String code, String? message, Object? details});
}
  • JSONMethodCodec: JSONMethodCodec编解码器依赖于JSONMessageCodec.在将MethodCall对象进行编码时,会首先将该对象转成JSON对象:{“method”:method,”args”:args},比如当前想要调用某个Channel的setVolum(5),其对应的MethodCall被被转成{"method":"setVolum","args":{"volum":5}},接下来使用JSONMessageCodec将其编码为二进制数据.
class JSONMethodCodec implements MethodCodec {

  const JSONMethodCodec();

  @override
  ByteData encodeMethodCall(MethodCall call) {
    return const JSONMessageCodec().encodeMessage(<String, Object?>{
      'method': call.method,
      'args': call.arguments,
    })!;
  }

  @override
  MethodCall decodeMethodCall(ByteData? methodCall) {
    final Object? decoded = const JSONMessageCodec().decodeMessage(methodCall);
    if (decoded is! Map)
      throw FormatException('Expected method call Map, got $decoded');
    final Object? method = decoded['method'];
    final Object? arguments = decoded['args'];
    if (method is String)
      return MethodCall(method, arguments);
    throw FormatException('Invalid method call: $decoded');
  }

  @override
  dynamic decodeEnvelope(ByteData envelope) {
    final Object? decoded = const JSONMessageCodec().decodeMessage(envelope);
    if (decoded is! List)
      throw FormatException('Expected envelope List, got $decoded');
    if (decoded.length == 1)
      return decoded[0];
    if (decoded.length == 3
        && decoded[0] is String
        && (decoded[1] == null || decoded[1] is String))
      throw PlatformException(
        code: decoded[0] as String,
        message: decoded[1] as String?,
        details: decoded[2],
      );
    if (decoded.length == 4
        && decoded[0] is String
        && (decoded[1] == null || decoded[1] is String)
        && (decoded[3] == null || decoded[3] is String))
      throw PlatformException(
        code: decoded[0] as String,
        message: decoded[1] as String?,
        details: decoded[2],
        stacktrace: decoded[3] as String?,
      );
    throw FormatException('Invalid envelope: $decoded');
  }

  @override
  ByteData encodeSuccessEnvelope(Object? result) {
    return const JSONMessageCodec().encodeMessage(<Object?>[result])!;
  }

  @override
  ByteData encodeErrorEnvelope({ required String code, String? message, Object? details}) {
    assert(code != null);
    return const JSONMessageCodec().encodeMessage(<Object?>[code, message, details])!;
  }
}
  • StandardMethodCodec: StandardMethodCodec的编解码器依赖于StandardMessageCodec,它也是MethodCodec的默认实现.当其编码在将MethodCall对象进行编码时,会将MethoCall对象的method和args依次使用StandardMessageCodec进行编码,然后写成二进制数据.
class StandardMethodCodec implements MethodCodec {
  
  const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]);

  /// The message codec that this method codec uses for encoding values.
  final StandardMessageCodec messageCodec;

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

  @override
  MethodCall decodeMethodCall(ByteData? methodCall) {
    final ReadBuffer buffer = ReadBuffer(methodCall!);
    final Object? method = messageCodec.readValue(buffer);
    final Object? arguments = messageCodec.readValue(buffer);
    if (method is String && !buffer.hasRemaining)
      return MethodCall(method, arguments);
    else
      throw const FormatException('Invalid method call');
  }

  @override
  ByteData encodeSuccessEnvelope(Object? result) {
    final WriteBuffer buffer = WriteBuffer();
    buffer.putUint8(0);
    messageCodec.writeValue(buffer, result);
    return buffer.done();
  }

  @override
  ByteData encodeErrorEnvelope({ required String code, String? message, Object? details}) {
    final WriteBuffer buffer = WriteBuffer();
    buffer.putUint8(1);
    messageCodec.writeValue(buffer, code);
    messageCodec.writeValue(buffer, message);
    messageCodec.writeValue(buffer, details);
    return buffer.done();
  }

  @override
  dynamic decodeEnvelope(ByteData envelope) {
    // First byte is zero in success case, and non-zero otherwise.
    if (envelope.lengthInBytes == 0)
      throw const FormatException('Expected envelope, got nothing');
    final ReadBuffer buffer = ReadBuffer(envelope);
    if (buffer.getUint8() == 0)
      return messageCodec.readValue(buffer);
    final Object? errorCode = messageCodec.readValue(buffer);
    final Object? errorMessage = messageCodec.readValue(buffer);
    final Object? errorDetails = messageCodec.readValue(buffer);
    final String? errorStacktrace = (buffer.hasRemaining) ? messageCodec.readValue(buffer) as String? : null;
    if (errorCode is String && (errorMessage == null || errorMessage is String) && !buffer.hasRemaining)
      throw PlatformException(code: errorCode, message: errorMessage as String?, details: errorDetails, stacktrace: errorStacktrace);
    else
      throw const FormatException('Invalid envelope');
  }
}

以上是MethodCodec以及其子类JSONMethodCodecStandardMethodCodec的源码实现。接下来, 我们一起看下MessageCodec的源码实现。

MessageCodec

MessageCodec用于基础数据跟二进制数据之间的编解码, BasicMessageChannel就是采用的它。

MessageCodec的子类有BinaryCodecStringCodecJSONMessageCodecStandardMessageCodec, 总共四种, 下面我们阅读源码, 看它们怎么实现的, 已经各种编码方式的使用.

  • 基础的MessageCodec: MessageCodec被设计为一个泛型接口,用于实现二进制数据ByteBuffer和不同类型数据之间的转换
abstract class MessageCodec<T> {
  /// 将指定类型message编码为二进制数据
  ByteData? encodeMessage(T message);
 
  /// 将二进制数据解码成指定类型
  T? decodeMessage(ByteData? message);
}
  • BinaryCodec: 用于二进制数据和二进制数据之间的编解码,在实现上什么也没有做,只是原封不动的将二进制数据返回
class BinaryCodec implements MessageCodec<ByteData> {
  /// Creates a [MessageCodec] with unencoded binary messages represented using
  /// [ByteData].
  const BinaryCodec();

  @override
  ByteData? decodeMessage(ByteData? message) => message;

  @override
  ByteData? encodeMessage(ByteData? message) => message;
}
  • StringCodec: 用于字符串与二进制数据之间的编解码,对于字符串采用UTF-8编码格式
class StringCodec implements MessageCodec<String> {
  /// Creates a [MessageCodec] with UTF-8 encoded String messages.
  const StringCodec();

  @override
  String? decodeMessage(ByteData? message) {
    if (message == null)
      return null;
    return utf8.decoder.convert(message.buffer.asUint8List(message.offsetInBytes, message.lengthInBytes));
  }

  @override
  ByteData? encodeMessage(String? message) {
    if (message == null)
      return null;
    final Uint8List encoded = utf8.encoder.convert(message);
    return encoded.buffer.asByteData();
  }
}
  • JSONMessageCodec: 用于数据类型与二进制数据之间的编解码,支持基础数据类型(boolean,char,double,float,int,long,short,String)以及List,Map.
class JSONMessageCodec implements MessageCodec<Object?> {
  // The codec serializes messages as defined by the JSON codec of the
  // dart:convert package. The format used must match the Android and
  // iOS counterparts.

  /// Creates a [MessageCodec] with UTF-8 encoded JSON messages.
  const JSONMessageCodec();

  @override
  ByteData? encodeMessage(Object? message) {
    if (message == null)
      return null;
      /// json编码message成字符串string
      /// StringCodec将字符串string编码成二进制数据
    return const StringCodec().encodeMessage(json.encode(message));
  }

  @override
  dynamic decodeMessage(ByteData? message) {
    if (message == null)
      return message;
      ///  StringCodec将二进制数据message解码成字符串string
      /// json将字符串string解码成dart基础数据类型, 返回。
    return json.decode(const StringCodec().decodeMessage(message)!);
  }
}
  • StandardMessageCodec: 用于数据类型和二进制数据之间的编解码,它也是BasicMessageChannel中默认使用的编解码器,支持基础数据类型(boolean,char,double,float,int,long,short,String),List,Map以及二进制数据
class StandardMessageCodec implements MessageCodec<Object?> {
 
  static const int _valueNull = 0;
  static const int _valueTrue = 1;
  static const int _valueFalse = 2;
  static const int _valueInt32 = 3;
  static const int _valueInt64 = 4;
  static const int _valueLargeInt = 5;
  static const int _valueFloat64 = 6;
  static const int _valueString = 7;
  static const int _valueUint8List = 8;
  static const int _valueInt32List = 9;
  static const int _valueInt64List = 10;
  static const int _valueFloat64List = 11;
  static const int _valueList = 12;
  static const int _valueMap = 13;
  static const int _valueFloat32List = 14;

  @override
  ByteData? encodeMessage(Object? message) {
    if (message == null)
      return null;
    final WriteBuffer buffer = WriteBuffer();
    writeValue(buffer, message);
    return buffer.done();
  }

  @override
  dynamic decodeMessage(ByteData? message) {
    if (message == null)
      return null;
    final ReadBuffer buffer = ReadBuffer(message);
    final Object? result = readValue(buffer);
    if (buffer.hasRemaining)
      throw const FormatException('Message corrupted');
    return result;
  }

  
  void writeValue(WriteBuffer buffer, Object? value) {
    if (value == null) {
      buffer.putUint8(_valueNull);
    } else if (value is bool) {
      buffer.putUint8(value ? _valueTrue : _valueFalse);
    } else if (value is double) {  
      buffer.putUint8(_valueFloat64);
      buffer.putFloat64(value);
    } else if (value is int) {
      if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
        buffer.putUint8(_valueInt32);
        buffer.putInt32(value);
      } else {
        buffer.putUint8(_valueInt64);
        buffer.putInt64(value);
      }
    } else if (value is String) {
      buffer.putUint8(_valueString);
      final Uint8List bytes = utf8.encoder.convert(value);
      writeSize(buffer, bytes.length);
      buffer.putUint8List(bytes);
    } else if (value is Uint8List) {
      buffer.putUint8(_valueUint8List);
      writeSize(buffer, value.length);
      buffer.putUint8List(value);
    } else if (value is Int32List) {
      buffer.putUint8(_valueInt32List);
      writeSize(buffer, value.length);
      buffer.putInt32List(value);
    } else if (value is Int64List) {
      buffer.putUint8(_valueInt64List);
      writeSize(buffer, value.length);
      buffer.putInt64List(value);
    } else if (value is Float32List) {
      buffer.putUint8(_valueFloat32List);
      writeSize(buffer, value.length);
      buffer.putFloat32List(value);
    } else if (value is Float64List) {
      buffer.putUint8(_valueFloat64List);
      writeSize(buffer, value.length);
      buffer.putFloat64List(value);
    } else if (value is List) {
      buffer.putUint8(_valueList);
      writeSize(buffer, value.length);
      for (final Object? item in value) {
        writeValue(buffer, item);
      }
    } else if (value is Map) {
      buffer.putUint8(_valueMap);
      writeSize(buffer, value.length);
      value.forEach((Object? key, Object? value) {
        writeValue(buffer, key);
        writeValue(buffer, value);
      });
    } else {
      throw ArgumentError.value(value);
    }
  }

  Object? readValue(ReadBuffer buffer) {
    if (!buffer.hasRemaining)
      throw const FormatException('Message corrupted');
    final int type = buffer.getUint8();
    return readValueOfType(type, buffer);
  }

  Object? readValueOfType(int type, ReadBuffer buffer) {
    switch (type) {
      case _valueNull:
        return null;
      case _valueTrue:
        return true;
      case _valueFalse:
        return false;
      case _valueInt32:
        return buffer.getInt32();
      case _valueInt64:
        return buffer.getInt64();
      case _valueFloat64:
        return buffer.getFloat64();
      case _valueLargeInt:
      case _valueString:
        final int length = readSize(buffer);
        return utf8.decoder.convert(buffer.getUint8List(length));
      case _valueUint8List:
        final int length = readSize(buffer);
        return buffer.getUint8List(length);
      case _valueInt32List:
        final int length = readSize(buffer);
        return buffer.getInt32List(length);
      case _valueInt64List:
        final int length = readSize(buffer);
        return buffer.getInt64List(length);
      case _valueFloat32List:
        final int length = readSize(buffer);
        return buffer.getFloat32List(length);
      case _valueFloat64List:
        final int length = readSize(buffer);
        return buffer.getFloat64List(length);
      case _valueList:
        final int length = readSize(buffer);
        final List<Object?> result = List<Object?>.filled(length, null, growable: false);
        for (int i = 0; i < length; i++)
          result[i] = readValue(buffer);
        return result;
      case _valueMap:
        final int length = readSize(buffer);
        final Map<Object?, Object?> result = <Object?, Object?>{};
        for (int i = 0; i < length; i++)
          result[readValue(buffer)] = readValue(buffer);
        return result;
      default: throw const FormatException('Message corrupted');
    }
  }

  void writeSize(WriteBuffer buffer, int value) {
    assert(0 <= value && value <= 0xffffffff);
    if (value < 254) {
      buffer.putUint8(value);
    } else if (value <= 0xffff) {
      buffer.putUint8(254);
      buffer.putUint16(value);
    } else {
      buffer.putUint8(255);
      buffer.putUint32(value);
    }
  }

  int readSize(ReadBuffer buffer) {
    final int value = buffer.getUint8();
    switch (value) {
      case 254:
        return buffer.getUint16();
      case 255:
        return buffer.getUint32();
      default:
        return value;
    }
  }
}

源码学习到此, 我们已经知道了, 消息被MessageCodecMethodCodec编码成二进制数据了, 那么这个二进制数据是怎样传输到iOS、Android平台的呢? 答案是有的, 信使BinaryMessenge调用send方法, 将二进制数据发送出去的。接下来, 我们一起去BinaryMessenger源码探究下。

重点讲解BinaryMessenger

回到开篇BasicMessageChannel发送消息的地方

class BasicMessageChannel<T> {

  const BasicMessageChannel(this.name, this.codec, { BinaryMessenger? binaryMessenger })
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MessageCodec<T> codec;

  /// The messenger which sends the bytes for this channel, not null.
  BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;
  final BinaryMessenger? _binaryMessenger;

 /// 2、发送消息
 /// codec先将消息message编码, binaryMessenger将编码后的消息发送给iOS、Android平台,并等待iOS、Android平台的回复消息。
  Future<T?> send(T message) async {
    return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
  }

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

由上面源码, send发送消息可以知道, 分为两步:

  • codec先将消息message编码
  • binaryMessenger将编码后的消息发送给iOS、Android平台,并等待iOS、Android平台的回复消息。

那么, 信使BinaryMessenger是怎样工作的呢? 我们跳进binaryMessenger.send()一探究竟。

BasicMessageChannel 采用的是默认的BinaryMessenger, BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;

源码如下:

class _DefaultBinaryMessenger extends BinaryMessenger {
  const _DefaultBinaryMessenger._();

  @override
  Future<void> handlePlatformMessage(
    String channel,
    ByteData? message,
    ui.PlatformMessageResponseCallback? callback,
  ) async {
    ui.channelBuffers.push(channel, message, (ByteData? data) {
      if (callback != null)
        callback(data);
    });
  }

  @override
  Future<ByteData?> send(String channel, ByteData? message) {
    final Completer<ByteData?> completer = Completer<ByteData?>();
 
    ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('during a platform message response callback'),
        ));
      }
    });
    return completer.future;
  }

  @override
  void setMessageHandler(String channel, MessageHandler? handler) {
    if (handler == null) {
      ui.channelBuffers.clearListener(channel);
    } else {
      ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
        ByteData? response;
        try {
          response = await handler(data);
        } catch (exception, stack) {

          FlutterError.reportError(FlutterErrorDetails(
            exception: exception,
            stack: stack,
            library: 'services library',
            context: ErrorDescription('during a platform message callback'),
          ));
        } finally {
          callback(response);
        }
      });
    }
  }
}

可以看到send方法最终去执行了ui.PlatformDispatcher.instance.sendPlatformMessage()。将参数传递给了ui.PlatformDispatcher.instancesendPlatformMessage方法, 并设置了reply。但是远远没有结束, 我们来看下ui.PlatformDispatcher的源码:

void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) {
  final String? error =
      _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
  if (error != null)
    throw Exception(error);
}

String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data)
    native 'PlatformConfiguration_sendPlatformMessage';

程序最终执行到了_sendPlatformMessage方法, 这里有个native关键字。这段代码的意思是, 这个接口是由native方法PlatformConfiguration_sendPlatformMessage导出成dart语言的,在dart中,方法名是_sendPlatformMessage, 最终我们找到c++的Flutter引擎源码

void PlatformConfiguration::RegisterNatives(
    tonic::DartLibraryNatives* natives) {
  natives->Register({
      {"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true},
      {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
      /// 第3步, 注册平台通信
      {"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4,
       true},
      {"PlatformConfiguration_respondToPlatformMessage",
       _RespondToPlatformMessage, 3, true},
      {"PlatformConfiguration_render", Render, 3, true},
      {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true},
      {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2,
       true},
      {"PlatformConfiguration_reportUnhandledException",
       ReportUnhandledException, 2, true},
      {"PlatformConfiguration_setNeedsReportTimings", SetNeedsReportTimings, 2,
       true},
      {"PlatformConfiguration_getPersistentIsolateData",
       GetPersistentIsolateData, 1, true},
      {"PlatformConfiguration_computePlatformResolvedLocale",
       _ComputePlatformResolvedLocale, 2, true},
  });
}

/// 引用了_SendPlatformMessage方法
void _SendPlatformMessage(Dart_NativeArguments args) { 
     /// 最终调用了SendPlatformMessage
    tonic::DartCallStatic(&SendPlatformMessage, args); 
}

/// 最终调用了SendPlatformMessage
Dart_Handle SendPlatformMessage(Dart_Handle window,
                                const std::string& name,
                                Dart_Handle callback,
                                Dart_Handle data_handle) {
  UIDartState* dart_state = UIDartState::Current();

   /// 1、只能在main iolate调用平台方法

  if (!dart_state->platform_configuration()) {
    return tonic::ToDart(
        "Platform messages can only be sent from the main isolate");
  }

  fml::RefPtr<PlatformMessageResponse> response;
  if (!Dart_IsNull(callback)) {
    response = fml::MakeRefCounted<PlatformMessageResponseDart>(
        tonic::DartPersistentValue(dart_state, callback),
        dart_state->GetTaskRunners().GetUITaskRunner());
  }
  
  /// 2、核心方法调用 
  if (Dart_IsNull(data_handle)) {
    dart_state->platform_configuration()->client()->HandlePlatformMessage(
        std::make_unique<PlatformMessage>(name, response));
  } else {
    tonic::DartByteData data(data_handle);
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
    dart_state->platform_configuration()->client()->HandlePlatformMessage(
        std::make_unique<PlatformMessage>(
            name, fml::MallocMapping::Copy(buffer, data.length_in_bytes()),
            response));
  }

  return Dart_Null();
}

dart_args.h

template <typename Sig>
void DartCall(Sig func, Dart_NativeArguments args) {
  DartArgIterator it(args);
  using Indices = typename IndicesForSignature<Sig>::type;
  /// 执行DartDispatcher派发操作
  DartDispatcher<Indices, Sig> decoder(&it);
  if (it.had_exception())
    return;
  decoder.Dispatch(func);
}

从c++源码分析看, SendPlatformMessage方法里面, 执行了HandlePlatformMessage类。最后, 执行到了 iOS平台源码

PlatformViewIOS完整代码

// |PlatformView|
void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr<flutter::PlatformMessage> message) {
  platform_message_router_.HandlePlatformMessage(std::move(message));
}

可以肯定地是, PlatformViewIOS对应的就是iOS平台的。PlatformViewIOS继续分发, platform_message_router_去执行HandlePlatformMessage(std::move(message))方法

PlatformMessageRouter

void PlatformMessageRouter::HandlePlatformMessage(
    fml::RefPtr<flutter::PlatformMessage> message) const {
  fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response();
  auto it = message_handlers_.find(message->channel());
  if (it != message_handlers_.end()) {
    FlutterBinaryMessageHandler handler = it->second;
    NSData* data = nil;
    if (message->hasData()) {
      data = GetNSDataFromVector(message->data());
    }
    handler(data, ^(NSData* reply) {
      if (completer) {
        if (reply) {
          completer->Complete(GetMappingFromNSData(reply));
        } else {
          completer->CompleteEmpty();
        }
      }
    });
  } else {
    if (completer) {
      completer->CompleteEmpty();
    }
  }
}

/// 这个SetMessageHandler在iOS、Android上接收消息的接口。特别重要!!!
void PlatformMessageRouter::SetMessageHandler(const std::string& channel,
                                              FlutterBinaryMessageHandler handler) {
  message_handlers_.erase(channel);
  if (handler) {
    message_handlers_[channel] =
        fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain};
  }
}

终于看到了我们iOS开发熟悉的mm文件了。由上述代码可知:

  • 这个方法接收PlatformMessage类型消息
  • message_handlers_里找到存储的channel对应的handler, 然后如果有数据就把数据转成NSData,最后执行handler,这里handler已经是OC代码定义的一个block了,block执行的时候会传递2个参数,一个是message,这个message自从执行到C++代码后就封装成了fml::RefPtr<PlatformMessage>类型的指针,期间Native层面一直未曾改变,第二个参数FlutterBinaryReply也是一个block,当completer存在就会调用completer->Complete或者completer->CompleteEmpty(),用于接收原生代码的回调。
  • PlatformMessageRouter类中的 SetMessageHandler方法是iOS平台接收并回复消息的重要方法, iOS平台都有实现。iOS平台定义了一个FlutterBinaryMessageHandlerblock方法, 来进行回调。

到这里就已经完成了Native层的调用。

参考资料

Flutter Channel通信原理(上)

深入Flutter技术内幕:Platform Channel设计与实现(一)

Flutter引擎源码

PlatformMessageRouter