Flutter进阶-引擎源码调试

4,677 阅读6分钟

指定ios_debug_sim_unopt引擎

1.首先新建一个flutter工程,找到ios目录下在Generated文件中指定调试的引擎

// ios配置文件指定引擎
//FLUTTER_ENGINE=/Users/xxx/engine/src
//LOCAL_ENGINE=ios_debug_sim_unopt
// ios配置文件指定引擎

当然前提是这里已经使用GN构建好了 image.png 2.此时运行iOS工程,设置断点 image.png 点击屏幕触发断点,跟踪这些断点可以找到-[FlutterViewController touchesBegan:withEvent:]这个方法,找到上面的ios_debug_sim_unopt的目录,打开flutter_engine工程,在FlutterViewController.m文件中找到了这个方法。 image.png image.png

这样通过调试debug的引擎可以一步一步来跟踪方法的实现,为我们更好的理解Flutter提供了一种方法。

Flutter Channel底层原理探究

Flutter作为一个灵活的UI框架,无论是iOS平台上的OC或Swift, 还是安卓平台上的Java或Kotlin都可以通过Platform Channel机制来与Flutter进行通讯。需要注意的是Platform Channel不依赖代码而成,而是建立在消息传递的方式上。实际上,它的工作模式和原理非常类似于基于二进制协议开发的网络服务

iOS channel原理

Flutter提供了三种Channel用作Flutter与iOS原生平台之间的数据传递 image.png

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

image.png

在创建一个channel后,不论是通过设置代理还是通过setHandle回调来进行消息处理,最终都会为该channel绑定一个FlutterBinaryMessengerHandler,并且以channel的name作为key,保存在一个map中。当接收到发送消息之后,会根据消息中携带的channel名称取出对应的FlutterBinaryMessengerHandler,交给BinaryMessenger处理,在ios平台上BinaryMessenger是一个名为FlutterBinaryMessenger协议

FlutterMethodChanneladdMethodCallDelegate: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

image.png

  • FlutterStandardMessageCodec:是BasicMessage Channel默认使用的编解码器,底层是用FlutterStandardReaderWriter实现的,用于数据类型和二进制之间的编解码。支持基础数据类型包括(bool/char/string/double/float/int/long/short/array/dictionary)以及二进制数据
  • FlutterBinaryCodec:用于二进制数据与二进制数据之间的编解码,在实现上只是原封不动的将接收到的二进制数据返回
  • FlutterStringCodec:用于字符串与二进制数据之间的编解码,字符串采用utf-8编码格式
  • FlutterJSONMessageCodec:用于数据类型与二进制数据之间的编解码,支持基础数据类型,在iOS端使用NSJSONSerialization作为序列化的工具

FlutterMethodCodec

  • FlutterMethodCodec用于二进制数据与方法调用FlutterMethodCall和返回结果之间的编解码,主要是用在FlutterMethodChannelFlutterEventChannel
@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对象进行编码时,会将Methodargs依次使用FlutterStandardReaderWriter进行编码,然后写成二进制数据。

深入编解码器原理

上面我们说过FlutterStandardMessageCodeFlutterStandardMethodCodec都是使用了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中的这个方法,在该方法中通常需要销毁一些无用的资源