1 channel原理
1. Flutter提供了三种Channel
用作Flutter与原生的数据传递
FlutterBasicMessageChannel:用作字符串和半结构换的数据传递
结构化的数据:包含预定义的数据类型、格式和结构的数据,eg:关系型数据库中数据表里的数据 半结构化数据:具有可识别的模式并可以解析的文本数据文件,eg:xml数据文件 非结构化数据:没有固定结构的数据,通常保存为不同类型的文件,eg:文本文档,图片,视频等
FlutterMethodChannel:用来调用方法(method invocation),包括从Flutter向原生平台发起方法调用,也支持从原生平台向Flutter发起方法调用。
- FlutterEventChannel: 用来支持数据流(streams)通信。”
2. 三种Channel都有以下三个成员变量
1. name:Channel名称
作为每个Channel的唯一标志。
在我们的Flutter应用中,通常会存在多个Platform Channel。那么这些Channel之间就是通过唯一标志name来区分。例如,使用FlutterMethodChannel发起方法调用时,就需要我们为MethodChannel指定对应的标示name。
2. messenger:消息信使(BinaryMessenger)
用作消息的发送和接收的工具,主要负责Flutter与原生之间的相互通讯。
通俗来讲,messenger就是咱们现在的外卖小哥。messenger负责把数据从Flutter送到iOS平台,或者从iOS传输数据到Flutter。尽管Flutter中存在三种不同用途的Channel,但是对应的沟通工具都是BinaryMessenger。
在创建一个Channel后,不论是通过设置代理(Delegate),还是通过setXXXXHandler:来进行消息处理。最终会为该Channel绑定一个FlutterBinaryMessageHandler。并以Channel的name作为key,保存在一个Map结构中。当接受到发送消息后,会根据消息中携带的Channel名称取出对应FlutterBinaryMessageHandler,并交由BinaryMessenger处理。
注意:在iOS平台BinaryMessenger是一个名为FlutterBinaryMessenger的协议。
以FlutterMethodChannel的addMethodCallDelegate:channel:为例:
该方法的底层代码,位于engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm:
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
channel:(FlutterMethodChannel*)channel {
// 实际上也是通过setMethodCallHandler:实现的
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[delegate handleMethodCall:call result:result];
}];
}
然后来看setMethodCallHandler:,代码位于engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm:
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
// 可以看到底层最终调用的都是同一个方法,此时的_messenger代表的是FlutterViewController
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
接下来来看FlutterViewController里的setMessageHandlerOnChannel:binaryMessageHandler:,代码位于engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm:
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSAssert(channel, @"The channel must not be null");
// 调到FlutterEngine里面的setMessageHandlerOnChannel
[_engine.get().binaryMessenger setMessageHandlerOnChannel:channel binaryMessageHandler:handler];
}
进入到FlutterEngine的setMessageHandlerOnChannel:binaryMessageHandler:,代码位于engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm:
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSParameterAssert(channel);
NSAssert(_shell && _shell->IsSetup(),
@"Setting a message handler before the FlutterEngine has been run.");
// 通过flutter::PlatformViewIOS获取PlatformMessageRouter
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
}
PlatformMessageRouter,代码位于engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h
// map结构
std::unordered_map<std::string, fml::ScopedBlock<FlutterBinaryMessageHandler>>
message_handlers_;
void PlatformMessageRouter::SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler) {
message_handlers_.erase(channel);
if (handler) {
// key-value形式
message_handlers_[channel] =
fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain};
}
}
3. Codec(编解码器)
在Channel中,messenger携带的数据需要在Dart层,Native(iOS/Android平台)层中传输,所以就需要一种与平台无关的数据协议。既能支持图片,又能支持文件等资源。因此官方最终采用了二进制字节流作为数据传输协议。
二进制字节流:发送方需要把数据编码成二进制数据,接收方再把数据解码成原始数据。而负责编解码操作的就是Codec。
FlutterMethodCodec:对FlutterMethodCall编解码
FlutterMethodCodec用于二进制数据与方法调用(FlutterMethodCall)和返回结果之间的编解码。主要用在FlutterMethodChannel和FlutterEventChannel中。
创建channel
监听channel
FlutterBinaryMessager协议定义
FlutterBinaryMessager协议实现未找到,项目调试
3 项目调试引擎
- 新建项目
- 配置引擎
- 遵循协议FlutterBinaryMessager
- 创建channel
- 断点调试进入函数体,找到-setMessageHandlerOnChannel:binaryMessageHandler:的实现
_engine.get().binaryMessager 是消息信使,创建channel时传递的binaryMessager
channel是创建channel时的name
handle是内部包含了外部handler的回调call
- 进入_engine.get().binaryMessager的-setMessageHandlerOnChannel:binaryMessageHandler:中
- 进入self.parent的-setMessageHandlerOnChannel:binaryMessageHandler:中
将channel作为key,handler作为value存储起来
2 codec编解器
在Flutter中有两种Codec:FlutterMessageCodec和FlutterMethodCodec
MessageCodec对message进行编解码
FlutterMessageCodec
用于二进制数据与基础数据之间的编解码,其中FlutterBasicMessageChannel中采用的就是该Codec。
// 用于实现二进制数据NSData和不同类型数据之间的转换
@protocol FlutterMessageCodec
/**
* Returns a shared instance of this `FlutterMessageCodec`.
*/
+ (instancetype)sharedInstance;
// 将指定的类型message编码为二进制数据
- (NSData* _Nullable)encode:(id _Nullable)message;
// 将二进制数据NSData解码成指定类型
- (id _Nullable)decode:(NSData* _Nullable)message;
@end
四种FlutterMessageCodec:
- FlutterJSONMessageCodec:用于数据类型与二进制数据之间的编解码,支持基础数据类型(bool、char、double、float、int、long、short、String、Array、Dictionary)。在iOS端使用NSJSONSerialization作为序列化的工具。
- FlutterBinaryCodec 二进制数据之间的编解码
- FlutterStringCodec 字符串与二进制数据之间的编解码,对于字符串采用UTF-8编码格式。
- FlutterStandardMessageCodec 默认编解码器,任意类型和二进制数据之间的编解码,原理是使用FlutterStandardReaderWriter实现的。用于数据类型和二进制数据之间的编解码。支持基础数据类型包(bool、char、double、float、int、long、short、String、Array、Dictionary)以及二进制数据。
FlutterBinaryCodec:用于二进制数据和二进制数据之间的编解码,在实现上只是原封不动的将接收到的二进制数据返回。
在FlutterStandardMethodCodec中最重要的两个方法是writeValue:和readValueOfType:。前者用于将value值写入到NSData中,后者从NSData中读取value值。在iOS中,将当前手机电量传递给Flutter的过程中。假设电量值为100。那么该值转换成二进制数据流程为:
首先向NSData中写入表示int32类型的标志值FlutterStandardFieldInt32。
因为int32占4个byte。将value值继续写入到NSData中。
当Flutter接受到该二进制数据后:
先读取第一个byte值。根据此值得知后面需要读取一个int32类型的数据。
随后读取后面4个byte。并将其转为dart类型中int类型。
FlutterStandardReaderWriter
读写数据:
@implementation FlutterStandardWriter {
NSMutableData* _data;
}
.......
- (void)writeAlignment:(UInt8)alignment {
UInt8 mod = _data.length % alignment;
if (mod) {
for (int i = 0; i < (alignment - mod); i++) {
[self writeByte:0];
}
}
}
“.......
/**
下面14个枚举值用来标志不同类型的数据,比如0表示NULL,3表示Int32类型。
在向NSData写入指定类型的数据时,需要首先写入类型标志。然后紧跟着写入具体的值。
在从NSData读取数据时,首先读取类型标志,然后读取具体的数值。
*/
typedef NS_ENUM(NSInteger, FlutterStandardField) {
FlutterStandardFieldNil,
FlutterStandardFieldTrue,
FlutterStandardFieldFalse,
FlutterStandardFieldInt32,
FlutterStandardFieldInt64,
FlutterStandardFieldIntHex,
FlutterStandardFieldFloat64,
FlutterStandardFieldString,
// The following must match the corresponding order from `FlutterStandardDataType`.
FlutterStandardFieldUInt8Data,
FlutterStandardFieldInt32Data,
FlutterStandardFieldInt64Data,
FlutterStandardFieldFloat64Data,
FlutterStandardFieldList,
FlutterStandardFieldMap,
};
// 根据数据类型,先向data中写入类型标志值
- (void)writeValue:(id)value {
if (value == nil || value == [NSNull null]) {
[self writeByte:FlutterStandardFieldNil];
} else if ([value isKindOfClass:[NSNumber class]]) {
CFNumberRef number = (CFNumberRef)value;
BOOL success = NO;
if (CFGetTypeID(number) == CFBooleanGetTypeID()) {
BOOL b = CFBooleanGetValue((CFBooleanRef)number);
[self writeByte:(b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)];
success = YES;
} else if (CFNumberIsFloatType(number)) {
Float64 f;
success = CFNumberGetValue(number, kCFNumberFloat64Type, &f);
if (success) {
// 1. 写入类型标志值
[self writeByte:FlutterStandardFieldFloat64];
// 2. 指定对齐方式,用0补足
[self writeAlignment:8];
// 3. f转为byte,写入到NSData中
[self writeBytes:(UInt8*)&f length:8];
}
} else if (CFNumberGetByteSize(number) <= 4) {
SInt32 n;
success = CFNumberGetValue(number, kCFNumberSInt32Type, &n);
if (success) {
[self writeByte:FlutterStandardFieldInt32];
[self writeBytes:(UInt8*)&n length:4];
}
} else if (CFNumberGetByteSize(number) <= 8) {
SInt64 n;
success = CFNumberGetValue(number, kCFNumberSInt64Type, &n);
if (success) {
[self writeByte:FlutterStandardFieldInt64];
[self writeBytes:(UInt8*)&n length:8];
}
}
if (!success) {
NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number));
NSAssert(NO, @"Unsupported value for standard codec");
}
} else if ([value isKindOfClass:[NSString class]]) {
NSString* string = value;
[self writeByte:FlutterStandardFieldString];
[self writeUTF8:string];
} else if ([value isKindOfClass:[FlutterStandardTypedData class]]) {
FlutterStandardTypedData* typedData = value;
[self writeByte:FlutterStandardFieldForDataType(typedData.type)];
[self writeSize:typedData.elementCount];
[self writeAlignment:typedData.elementSize];
[self writeData:typedData.data];
} else if ([value isKindOfClass:[NSData class]]) {
[self writeValue:[FlutterStandardTypedData typedDataWithBytes:value]];
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray* array = value;
[self writeByte:FlutterStandardFieldList];
[self writeSize:array.count];
for (id object in array) {
[self writeValue:object];
}
} else if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary* dict = value;
[self writeByte:FlutterStandardFieldMap];
[self writeSize:dict.count];
for (id key in dict) {
[self writeValue:key];
[self writeValue:[dict objectForKey:key]];
}
} else {
NSLog(@"Unsupported value: %@ of type %@", value, [value class]);
NSAssert(NO, @"Unsupported value for standard codec");
}
}
@end
@implementation FlutterStandardReader {
NSData* _data;
NSRange _range;
}
- (instancetype)initWithData:(NSData*)data {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_data = [data retain];
_range = NSMakeRange(0, 0);
return self;
}
- (void)dealloc {
[_data release];
[super dealloc];
}
.......
// `writeValue:`方法反向过程,原理一致
- (nullable id)readValueOfType:(UInt8)type {
// 根据标志位,取出数据类型
FlutterStandardField field = (FlutterStandardField)type;
switch (field) {
case FlutterStandardFieldNil:
return nil;
case FlutterStandardFieldTrue:
return @YES;
case FlutterStandardFieldFalse:
return @NO;
case FlutterStandardFieldInt32: {
SInt32 value;
// 根据数据类型和长度,从NSData中读出数据,包装成NSNumber
[self readBytes:&value length:4];
return @(value);
}
case FlutterStandardFieldInt64: {
SInt64 value;
[self readBytes:&value length:8];
return @(value);
}
case FlutterStandardFieldFloat64: {
Float64 value;
[self readAlignment:8];
[self readBytes:&value length:8];
return [NSNumber numberWithDouble:value];
}
case FlutterStandardFieldIntHex:
case FlutterStandardFieldString:
return [self readUTF8];
case FlutterStandardFieldUInt8Data:
case FlutterStandardFieldInt32Data:
case FlutterStandardFieldInt64Data:
case FlutterStandardFieldFloat64Data:
return [self readTypedDataOfType:FlutterStandardDataTypeForField(field)];
case FlutterStandardFieldList: {
UInt32 length = [self readSize];
NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
for (UInt32 i = 0; i < length; i++) {
id value = [self readValue];
[array addObject:(value == nil ? [NSNull null] : value)];
}
return array;
}
case FlutterStandardFieldMap: {
UInt32 size = [self readSize];
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:size];
for (UInt32 i = 0; i < size; i++) {
id key = [self readValue];
id val = [self readValue];
[dict setObject:(val == nil ? [NSNull null] : val)
forKey:(key == nil ? [NSNull null] : key)];
}
return dict;
}
default:
NSAssert(NO, @"Corrupted standard message");
}
}
@end
@implementation FlutterStandardReaderWriter
- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data {
return [[[FlutterStandardWriter alloc] initWithData:data] autorelease];
}
- (FlutterStandardReader*)readerWithData:(NSData*)data {
return [[[FlutterStandardReader alloc] initWithData:data] autorelease];
}
@end
FlutterMethodCodec
FLUTTER_EXPORT
@protocol FlutterMethodCodec
/**
* Provides access to a shared instance this codec.
*
* @return The shared instance.
*/
+ (instancetype)sharedInstance;
// 将FlutterMethodCall编码为二进制NSData
- (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall;
// 将二进制NSData methodCall解码为FlutterMethodCall
- (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall;
// 将正常响应结果result编码为二进制
- (NSData*)encodeSuccessEnvelope:(id _Nullable)result;
// 将错误响应提示编码为二进制NSData
- (NSData*)encodeErrorEnvelope:(FlutterError*)error;
// 将二进制数据NSData解码,失败返回`FlutterError`
- (id _Nullable)decodeEnvelope:(NSData*)envelope;
@end
两种FlutterMethodCodec:
- FlutterStandardMethodCodec 原理同FlutterStandardMessageCodec一样,是使用FlutterStandardReaderWriter实现的 ```objectivec @implementation FlutterStandardMethodCodec { FlutterStandardReaderWriter* _readerWriter; }
- (instancetype)sharedInstance {
static id _sharedInstance = nil;
if (!_sharedInstance) {
//初始化readerWriter,用来编码和解码
FlutterStandardReaderWriter* readerWriter =
_sharedInstance = [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter]; } return _sharedInstance; }[[[FlutterStandardReaderWriter alloc] init] autorelease];
(NSData)encodeMethodCall:(FlutterMethodCall)call { …….. // 将call写入二进制字节流中 FlutterStandardWriter* writer = [_readerWriter writerWithData:data]; [writer writeValue:call.method]; [writer writeValue:call.arguments]; …….. }
(NSData)encodeSuccessEnvelope:(id)result { …….. FlutterStandardWriter writer = [_readerWriter writerWithData:data]; [writer writeByte:0]; [writer writeValue:result]; …….. }
(NSData)encodeErrorEnvelope:(FlutterError)error { …….. FlutterStandardWriter* writer = [_readerWriter writerWithData:data]; [writer writeByte:1]; [writer writeValue:error.code]; [writer writeValue:error.message]; [writer writeValue:error.details]; …….. }
(FlutterMethodCall)decodeMethodCall:(NSData)message { // 从二进制字节流message中读取数据,转成FlutterMethodCall FlutterStandardReader* reader = [_readerWriter readerWithData:message]; …….. }
(id)decodeEnvelope:(NSData)envelope { FlutterStandardReader reader = [_readerWriter readerWithData:envelope]; …….. }
@end
- FlutterJSONMethodCodec
<br />
<a name="xCXbB"></a>
#### FlutterMethodCall
代表从Flutter端发起的方法调用。方法调用包括:方法名、方法参数以及方法返回结果。因此和FlutterMessageCodec相比,FlutterMethodCodec中多了两个处理调用结果的方法:<br />方法调用成功:使用encodeSuccessEnvelope:编码result。<br />方法调用失败:使用encodeErrorEnvelope:()编码FlutterError。<br />decodeEnvelope:方法则用于解码iOS平台代码调用Dart中方法的结。比如通过FlutterMethodChannel调用Flutter中的方法,且获得其返回结果。
当前在FlutterMethodCodec有两种实现:
FlutterJSONMethodCodec:FlutterJSONMethodCodec编解码器依赖于FlutterJSONMethodCodec。在将FlutterMethodCall对象进行编码时,会首先将该对象转成JSON对象:
```objectivec
{
"method":method,
"args":args
}
代码位于engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm:
- (NSData*)encodeMethodCall:(FlutterMethodCall*)call { return [[FlutterJSONMessageCodec sharedInstance] encode:@{
@"method" : call.method,
@"args" : [self wrapNil:call.arguments],
}];
}
其在编码调用结果时,会将其转化为一个数组,调用成功为[result],调用失败为[code, message, details]。
- (NSData*)encodeSuccessEnvelope:(id)result {
// 数组
return [[FlutterJSONMessageCodec sharedInstance] encode:@[ [self wrapNil:result] ]];
}
- (NSData*)encodeErrorEnvelope:(FlutterError*)error {
// 数组
return [[FlutterJSONMessageCodec sharedInstance] encode:@[
error.code,
[self wrapNil:error.message],
[self wrapNil:error.details],
]];
}
当前想要调用某个Channel的setVolum(12)。其对应的MethodCall被被转成:
{
"method":"setVolum",
"args":{
"volum":5
}
}
接下来使用FlutterJSONMessageCodec将其编码为二进制数据。
FlutterStandardMethodCodec:它是FlutterMethodCodec的默认实现。当其编码在将FlutterMethodCall对象进行编码时,会将method和args依次使用FlutterStandardReaderWriter进行编码,然后写成二进制数据。
注意:Flutter新版本的StandardMethodCodec不再依赖StandardMessageCodec对method和args进行编码。而是使用公共类FlutterStandardReaderWriter进行编码。
3 Handler消息处理
Flutter中定义了一套Handler用于处理经过Codec解码后消息。在使用Channel时,需要为其设置对应的Handler。本质上就是为其注册一个对应FlutterBinaryMessageHandler,二进制数据被FlutterBinaryMessageHandler进行处理。首先使用Codec进行解码操作,然后再分发给具体Handler进行处理。与三种Platform Channel相对应,Flutter中也定义了三种Handler:
FlutterMessageHandler
用于处理字符串或者半结构化消息,定义在FlutterBasicMessageChannel中。
// 处理来自Flutter中的消息,有两个参数:id类型的消息以及用于异步返回的FlutterReply
typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback);
FlutterMethodCallHandler
用于处理方法调用,定义在MethodChannel中。
代码位于engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm:
// result用来异步提交调用结果。
typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call, FlutterResult result)
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
// Make sure the block captures the codec, not self.
NSObject<FlutterMethodCodec>* codec = _codec;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
handler(call, ^(id result) {
// 如果返回的是FlutterMethodNotImplemented表明此次调用未找到方法。
if (result == FlutterMethodNotImplemented)
callback(nil);
// 如果返回的是FlutterError表明调用失败。
else if ([result isKindOfClass:[FlutterError class]])
callback([codec encodeErrorEnvelope:(FlutterError*)result]);
else
callback([codec encodeSuccessEnvelope:result]);
});
};
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];}
FlutterStreamHandler
用于事件流(Stream)通信,定义在EventChannel中。StreamHandler用于事件流的通信,通常是用于平台主动向Flutter发送事件通知。在iOS中是一个FlutterStreamHandler协议。
@protocol FlutterStreamHandler
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events;
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments;
@end
在FlutterStreamHandler存在两个方法:
代码位于:
static void SetStreamHandlerMessageHandlerOnChannel(NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec) {
__block FlutterEventSink currentSink = nil;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
if ([call.method isEqual:@"listen"]) {
......
FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
......
} else if ([call.method isEqual:@"cancel"]) {
......
FlutterError* error = [handler onCancelWithArguments:call.arguments];
......
}
[messenger setMessageHandlerOnChannel:name binaryMessageHandler:messageHandler];
}
onListenWithArguments:。当Flutter端开始监听平台事件时,会向平台发起一次MethodCall,其中方法名为listen,也就是最终会调用FlutterStreamHandler中的onListenWithArguments:方法。该方法中接受两个参数,其中eventSink的参数可用于向Flutter发送事件(实际上还是通过BinaryMessager)。
onCancelWithArguments:。当Flutter开始停止监听平台事件时,会再向平台发起一次MethodCall,其中方法名为cancel,最终会调用[…]