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>*)delegatechannel:(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代表的是FlutterViewControllerif (!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*)channelbinaryMessageHandler:(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*)channelbinaryMessageHandler:(FlutterBinaryMessageHandler)handler {NSParameterAssert(channel);NSAssert(_shell && _shell->IsSetup(),@"Setting a message handler before the FlutterEngine has been run.");// 通过flutter::PlatformViewIOS获取PlatformMessageRouterself.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]);elsecallback([codec encodeSuccessEnvelope:result]);});};[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];}
FlutterStreamHandler
用于事件流(Stream)通信,定义在EventChannel中。StreamHandler用于事件流的通信,通常是用于平台主动向Flutter发送事件通知。在iOS中是一个FlutterStreamHandler协议。
@protocol FlutterStreamHandler- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)argumentseventSink:(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,最终会调用[…]
