在上一篇文章《Flutter引擎——下载、编译和调试》中,我们已经可以调试引擎代码了;而在《Flutter与原生工程的混合开发》中,我们使用到了FlutterMethodChannel。本文就通过Flutter引擎代码的调试来研究一下channel的原理。
一、Channel的创建
首先,我创建了一个FlutterMethodChannel实例对象:
然后我想看一下methodChannelWithName方法的实现,点进去之后:
可以看到,只定位到了方法的声明中,方法的实现定位不到了。
那么如何找到该方法的实现呢?
其中最简单的方式就是找到下载到本地的Flutter引擎源代码,然后去查找对应的方法名就可以了,如下:
双击打开该工程,搜索methodChannelWithName即可:
我们今天通过另外一种断点的方式来进行调试。需要注意的是,要通过打断点的方式来调试Flutter引擎源码,就一定要将自己编译的本地Flutter引擎源码挂载到当前的Flutter项目当中,不然是定位不到对应的源码的。
首先在对应地方打个断点:
运行之后断到这里,然后在控制台通过如下lldb指令打断点:
代码语言:javascript复制br set -n "[FlutterMethodChannel methodChannelWithName:binaryMessenger:]"
然后点击下一步,就可以定位到对应的源码地方了:
接下来我们就一步一步点进去:
代码语言:javascript复制 (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的源码:
代码语言:javascript复制 (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,其源码是:
代码语言:javascript复制- (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的时候,会传递三个参数,如下:
代码语言:javascript复制 (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是什么,下面