在上一篇文章《Flutter引擎——下载、编译和调试》中,我们已经可以调试引擎代码了;而在《Flutter与原生工程的混合开发》中,我们使用到了FlutterMethodChannel。本文就通过Flutter引擎代码的调试来研究一下channel的原理。
一、Channel的创建
首先,我创建了一个FlutterMethodChannel实例对象:
然后我想看一下methodChannelWithName方法的实现,点进去之后:
可以看到,只定位到了方法的声明中,方法的实现定位不到了。
那么如何找到该方法的实现呢?
其中最简单的方式就是找到下载到本地的Flutter引擎源代码,然后去查找对应的方法名就可以了,如下:
双击打开该工程,搜索methodChannelWithName即可:
我们今天通过另外一种断点的方式来进行调试。需要注意的是,要通过打断点的方式来调试Flutter引擎源码,就一定要将自己编译的本地Flutter引擎源码挂载到当前的Flutter项目当中,不然是定位不到对应的源码的。
首先在对应地方打个断点:
运行之后断到这里,然后在控制台通过如下lldb指令打断点:
br set -n "[FlutterMethodChannel methodChannelWithName:binaryMessenger:]"
然后点击下一步,就可以定位到对应的源码地方了:
接下来我们就一步一步点进去:
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
return [FlutterMethodChannel methodChannelWithName:name binaryMessenger:messenger codec:codec];
}
可以看到,+ (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
会调用+ (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMethodCodec>*)codec
在+ (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger中创建了一个默认的FlutterStandardMethodCodec单例对象。FlutterStandardMethodCodec是一个解码器,下面会做详细介绍。
接下来看一下+ (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMethodCodec>*)codec的源码:
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
return [[[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger
codec:codec] autorelease];
}
可以看到,里面调用了- (instancetype)initWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMethodCodec>*)codec,其源码是:
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
return self;
}
可以看到,channel的创建以及初始化是比较简单的。
接下来我们研究一下channel的监听。
二、channel的监听
我们是通过setMethodCallHandler方法来监听channel事件,如下:
我们通过打断点点进去,找到了setMethodCallHandler的源码:
可以看到,最终会走到setMessageHandlerOnChannel: binaryMessageHandler:函数,接下来继续通过断点走进该函数的实现:
接着点进去:
再点进去:
再点进去:
可以看到,channel作为key,handler作为value,存进了message-handlers这个Map中。实际上,在外界每一个channel都会有一个作为唯一标识的channelName,因此在设置回调的时候就要将这个回调与channel的唯一标识进行一一对应。
三、channel的编码
我们在创建channel的时候,会传递三个参数,如下:
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec;
其中name和messenger在前面已经说过了,现在来聊一聊codec。
codec是消息编解码器,它会对你的数据类型进行编解码。比如,Swift中的Dictionary、OC中的NSDictionary以及Java中的Map,对应到Dart中都是Map,在不同的语言中其实现肯定是不一样的,那么他们是如何对应起来的呢,这就需要用到Codec了。
在Flutter中,定义了两种Codec:MessageCodec和MethodCodec。我们接下来以iOS中为例来给大家做介绍。
1,MessageCodec
可以看到,FlutterStandardMethodCodec是一个protocol协议,这个协议里面除了有一个单例获取方法之外,还有一个编码方法(用于将OC类型数据编码成二进制数据),和一个解码方法(用于将二进制数据解码成OC类型数据)
实现FlutterStandardMethodCodec协议的类有如下几个:
(1)FlutterBinaryCodec,用于二进制数据和二进制数据之间的编解码
(2)FlutterJSONMessageCodec,JSON转二进制,二进制转JSON
(3)FlutterStandardMessageCodec,Flutter默认的编解码器,用于任意的OC数据类型和二进制之间的编解码。
现在我们看一下FlutterStandardMessageCodec的源码:
可以看到,FlutterStandardMessageCodec类型的单例对象最终是通过FlutterStandardReaderWriter类型的一个对象来生成的。那么这里的FlutterStandardReaderWriter是什么呢?它有什么存在的必要性呢?且听我慢慢道来。
接下来再看一下FlutterStandardMethodCodec的源码(至于FlutterStandardMethodCodec是什么,下面👇会做介绍的):
可以看到,FlutterStandardMethodCodec类型的单例对象最终也是通过FlutterStandardReaderWriter类型的一个对象来生成的。
而通过FlutterStandardReaderWriter这个名称我们也能理解,这个类里面实际上就是数据的读写操作,也就是说,Flutter将编解码器的核心逻辑封装、抽离到了FlutterStandardReaderWriter层(读,即解码;写,即编码)。
(4)FlutterStringCodec,专门用于字符串与二进制数据之间的编解码,编码格式为UTF-8
2,MethodCodec
可以看到,FlutterMethodCodec是一个protocol协议,该协议里面,除了单例的获取方法之外,还有如下方法:
(1)encodeMethodCall,用于将OC的方法调用数据编码成二进制。
它编码的对象是FlutterMethodCall,而FlutterMethodCall的定义如下:
可以看到,它里面就俩参数,一个方法名method,还有一个参数arguments,而一个OC方法实际上就是由这两个属性组成的。
(2)decodeMethodCall,用于将二进制解码成OC的方法
(3)encodeSuccessEnvelope,在EventChannel中将成功的结果编码成二进制
(4)encodeErrorEnvelope,在EventChannel中将失败的结果编码成二进制
(5)decodeEnvelope,在EventChannel中,将二进制数据解码成OC类型的结果
可以看到,FlutterMethodCodec是对方法调用的编解码解析,适用于MethodChannel和EventChannel。其中,(1)和(2)用于MethodChannel中,(3)、(4)和(5)用于EventChannel中。
实现FlutterMethodCodec协议的类有如下两个:
(1)FlutterJSONMethodCodec
通过JSON格式和二进制转换数据
(2)FlutterStandardMethodCodec
标准格式是通过MethodCall转二进制或者二进制转MethodCall来使用。
以上。