指定ios_debug_sim_unopt
引擎
1.首先新建一个flutter工程,找到ios目录下在Generated
文件中指定调试的引擎
// ios配置文件指定引擎
//FLUTTER_ENGINE=/Users/xxx/engine/src
//LOCAL_ENGINE=ios_debug_sim_unopt
// ios配置文件指定引擎
当然前提是这里已经使用GN构建好了
2.此时运行iOS工程,设置断点
点击屏幕触发断点,跟踪这些断点可以找到
-[FlutterViewController touchesBegan:withEvent:]
这个方法,找到上面的ios_debug_sim_unopt
的目录,打开flutter_engine
工程,在FlutterViewController.m
文件中找到了这个方法。
这样通过调试debug的引擎可以一步一步来跟踪方法的实现,为我们更好的理解Flutter提供了一种方法。
Flutter Channel
底层原理探究
Flutter作为一个灵活的UI框架,无论是iOS平台上的OC或Swift, 还是安卓平台上的Java或Kotlin都可以通过Platform Channel
机制来与Flutter进行通讯。需要注意的是Platform Channel
不依赖代码而成,而是建立在消息传递的方式上。实际上,它的工作模式和原理非常类似于基于二进制协议开发的网络服务
iOS channel原理
Flutter提供了三种Channel用作Flutter与iOS原生平台之间的数据传递
FlutterMethodChannel(name: "one", binaryMessenger: self.flutterVc as! FlutterBinaryMessenger)
FlutterBasicMessageChannel(name: "messageChannel", binaryMessenger: self.flutterVc.binaryMessenger)
三种channel分别带来不同的作用,但是设计上大同小异,都有以下几个成员变量
name
:channel
名称,作为每个channel
的唯一标志。在flutter
应用中,通常会存在多个Platform Channel
,这些channel
之间就是通过唯一标志name
来区分。例如FlutterMethodChannel
发起方法调用时,就需要我们为MethodChannel
指定对应的标志name
- messenger:消息信使(
BinaryMessenger
)用作消息的发送和接收的工具,主要负责flutter与原生之间的相互通信,尽管flutter中存在三种不同的Channel,但是对应的沟通工具都是BinaryMessenger
在创建一个channel后,不论是通过设置代理还是通过setHandle
回调来进行消息处理,最终都会为该channel绑定一个FlutterBinaryMessengerHandler
,并且以channel的name作为key,保存在一个map中。当接收到发送消息之后,会根据消息中携带的channel名称取出对应的FlutterBinaryMessengerHandler
,交给BinaryMessenger
处理,在ios平台上BinaryMessenger
是一个名为FlutterBinaryMessenger
协议
以FlutterMethodChannel
的addMethodCallDelegate:channel:
为例
addMethodCallDelegate:channel:
-> setMethodCallHandler:handler
-> setMessageHandlerOnChannel:binaryMessageHandler:
-> PlatformMessageRouter::SetMessageHandler
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};
}
}
通过这个方法链也验证了上面说的每一个FlutterBinaryMessengerHandler
的key值都是channel的name
- Codec(编解码器):在channel中,messenger携带的数据需要在Dart层和Native层传输,所以就需要一种与平台无关的数据协议,既能支持图片又能支持文件等资源。因此官方采用了二进制字节流作为数据传输协议。二进制字节流:发送方需要把数据编写成二进制数据,接收方再把数据解码成原始数据,而负责编解码操作的就是Codec。在flutter中有两种Codec
FlutterMessageCodec
FlutterMessageCodec
:对message进行编解码,用于二进制与基础数据之间的编解码,其中FlutterBasicMessenger
采用的就是这个Codec,在flutter中,MessageCodec有多种实现
@protocol FlutterMessageCodec
+ (instancetype)sharedInstance;
// 将指定类型的message编码为二进制数据
- (NSData* _Nullable)encode:(id _Nullable)message;
// 将二进制数据NSData解码成指定类型
- (id _Nullable)decode:(NSData* _Nullable)message;
@end
FlutterStandardMessageCodec
:是BasicMessage Channel
默认使用的编解码器,底层是用FlutterStandardReaderWriter
实现的,用于数据类型和二进制之间的编解码。支持基础数据类型包括(bool/char/string/double/float/int/long/short/array/dictionary)以及二进制数据FlutterBinaryCodec
:用于二进制数据与二进制数据之间的编解码,在实现上只是原封不动的将接收到的二进制数据返回FlutterStringCodec
:用于字符串与二进制数据之间的编解码,字符串采用utf-8
编码格式FlutterJSONMessageCodec
:用于数据类型与二进制数据之间的编解码,支持基础数据类型,在iOS端使用NSJSONSerialization
作为序列化的工具
FlutterMethodCodec
FlutterMethodCodec
用于二进制数据与方法调用FlutterMethodCall
和返回结果之间的编解码,主要是用在FlutterMethodChannel
和FlutterEventChannel
中
@protocol FlutterMethodCodec
+ (instancetype)sharedInstance;
// 将FlutterMethodCall编码为二进制NSData
- (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall;
// 将二进制NSData解码为FlutterMethodCall
- (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall;
// 将正常响应结果result编码为二进制
- (NSData*)encodeSuccessEnvelope:(id _Nullable)result;
// 将错误响应提示编码为二进制NSData
- (NSData*)encodeErrorEnvelope:(FlutterError*)error;
// 将二进制数据NSData解码,失败返回 FlutterError
- (id _Nullable)decodeEnvelope:(NSData*)envelope;
@end
FlutterMethodCall
代表从Flutter端发起的方法调用,方法调用包括:方法名、参数以及返回结果。因此和FlutterMessageCodec
相比,FlutterMethodCodec
中多了两个处理调用结果的方法
当前在FlutterMethodCodec
有两种实现
FlutterJSONMethodCodec
:在将FlutterMethodCall
对象进行编码时,会首先将该对象转换成JSON对象
@interface FlutterJSONMethodCodec : NSObject <FlutterMethodCodec>
- (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],
]];
}
FlutterStandardMethodCodec
:是FlutterMethodCodec
的默认实现,当其编码在将FlutterMethodCall
对象进行编码时,会将Method
和args
依次使用FlutterStandardReaderWriter
进行编码,然后写成二进制数据。
深入编解码器原理
上面我们说过FlutterStandardMessageCode
和FlutterStandardMethodCodec
都是使用了FlutterStandardReaderWriter
进行编码,那么我们来看下FlutterStandardMethodCodec
的定义
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
if (!_sharedInstance) {
FlutterStandardReaderWriter* readerWriter =
[[[FlutterStandardReaderWriter alloc] init] autorelease];
_sharedInstance = [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter];
}
return _sharedInstance;
}
重点分析FlutterStandardReaderWriter
怎么来编解码的
@implementation FlutterStandardReaderWriter
- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data {
return [[[FlutterStandardWriter alloc] initWithData:data] autorelease];
}
- (FlutterStandardReader*)readerWithData:(NSData*)data {
return [[[FlutterStandardReader alloc] initWithData:data] autorelease];
}
@end
// 15个枚举值用来标识不同的数据类型,0是null,3表示Int32类型
// 在向NSData写入指定类型的数据时,需要首先写入标志位,然后紧接着写入值
// 在从NSData读取数据时,首先读取类型标志,然后读取具体的值
typedef NS_ENUM(NSInteger, FlutterStandardField) {
FlutterStandardFieldNil,
FlutterStandardFieldTrue,
FlutterStandardFieldFalse,
FlutterStandardFieldInt32,
FlutterStandardFieldInt64,
FlutterStandardFieldIntHex,
FlutterStandardFieldFloat64,
FlutterStandardFieldString,
FlutterStandardFieldUInt8Data,
FlutterStandardFieldInt32Data,
FlutterStandardFieldInt64Data,
FlutterStandardFieldFloat64Data,
FlutterStandardFieldList,
FlutterStandardFieldMap,
FlutterStandardFieldFloat32Data,
};
readerWithData:
从NSData中读取value值
writerWithData:
用于将value值写入到NSData中
Handler
消息处理
Flutter中定义了一套handler用于处理经过Codec
解码后的消息,在使用channel时,需要为其设置对应的handler,本质上就是为其注册一个对应的FlutterBinaryMessageHandler
,二进制数据被FlutterBinaryMessageHandler
进行处理,首先使用Codec
进行解码操作,然后再分发给具体的Handler进行处理。与三种Platform Channel
相对应,Flutter
中也定义了三种Handler
FlutterMessageHandler
用于处理字符串或者半结构化消息
typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback);
FlutterMethodCallHandler
用于处理方法调用FlutterStreamHandler
用于事件流通信,通常是用于平台主动向Flutter发送事件通知
@protocol FlutterStreamHandler
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events;
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments;
@end
onListenWithArguments:eventSink:
当Flutter端开始监听平台事件时,会向平台发起一次MethodCall
其中方法名为listen,也就是最终会调用FlutterStreamHandler
中的这个方法,其中eventSink
的参数可用于向Flutter发送事件
onCancelWithArguments:
当Flutter端停止监听平台事件时,会向平台发起一次MethodCall
其中方法名为cancel,也就是最终会调用FlutterStreamHandler
中的这个方法,在该方法中通常需要销毁一些无用的资源