上篇文章, 我们一起学习了官方文档中, 是怎样使用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
以及其子类JSONMethodCodec
、StandardMethodCodec
的源码实现。接下来, 我们一起看下MessageCodec的源码实现。
MessageCodec
MessageCodec用于基础数据跟二进制数据之间的编解码, BasicMessageChannel
就是采用的它。
MessageCodec
的子类有BinaryCodec
、StringCodec
、JSONMessageCodec
、StandardMessageCodec
, 总共四种, 下面我们阅读源码, 看它们怎么实现的, 已经各种编码方式的使用.
- 基础的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;
}
}
}
源码学习到此, 我们已经知道了, 消息被MessageCodec
、MethodCodec
编码成二进制数据了, 那么这个二进制数据是怎样传输到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.instance
的sendPlatformMessage
方法, 并设置了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();
}
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平台源码
// |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))
方法
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平台定义了一个FlutterBinaryMessageHandler
block方法, 来进行回调。
到这里就已经完成了Native层的调用。