The specifiedios_debug_sim_unoptengine

1. Create a Flutter project and find the engine Generated in the ios directory

// Ios profile specifies the engine
//FLUTTER_ENGINE=/Users/xxx/engine/src
//LOCAL_ENGINE=ios_debug_sim_unopt
// Ios profile specifies the engine
Copy the code

Assuming, of course, that this is already built using GN2. Run the iOS project and set a breakpointClick on the screen to trigger breakpoints and trace these breakpoints to be found-[FlutterViewController touchesBegan:withEvent:]This way, find the top oneios_debug_sim_unoptDirectory, openflutter_engineEngineering,FlutterViewController.mThe method is found in the file.

In this way, the debug engine can track the implementation of Flutter step by step, providing a better understanding of Flutter.

Flutter ChannelExploration of underlying principles

As a flexible UI framework, Flutter can communicate with Flutter via Platform Channel, whether it is OC or Swift on iOS, Java or Kotlin on Android. It is important to note that Platform Channel is not built on code, but on messaging. In fact, its working mode and principle are very similar to network services developed based on binary protocols

IOS channel principle

Flutter provides three channels for data transfer between Flutter and the iOS native platform

FlutterMethodChannel(name: "one", binaryMessenger: self.flutterVc as! FlutterBinaryMessenger)
FlutterBasicMessageChannel(name: "messageChannel", binaryMessenger: self.flutterVc.binaryMessenger)
Copy the code

The three channels have different functions respectively, but they are similar in design, with the following member variables

  • name: channelName as eachchannelThe only sign of. influtterIn an application, there are usually more than onePlatform Channel, thesechannelBetween is the only sign throughnameTo distinguish. For example,FlutterMethodChannelWhen a method call is made, we need to callMethodChannelSpecify the corresponding flagname
  • A messenger is a messenger.BinaryMessengerUsed as a tool for sending and receiving messages, mainly responsible for the communication between flutter and its native. Although there are three different channels in flutter, the corresponding communication tools are all the sameBinaryMessenger

After creating a channel, either by setting up a broker or by setHandle callback for message processing, Bundle a FlutterBinaryMessengerHandler eventually for the channel, and channel name as the key, stored in a map. When to receive after sending the message, according to the channel of the carried in the message name out corresponding FlutterBinaryMessengerHandler, to BinaryMessenger processing, On ios BinaryMessenger is a protocol called FlutterBinaryMessenger

With FlutterMethodChannel addMethodCallDelegate: channel: for example 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}; }}Copy the code

Through this method chain also verified the above said every FlutterBinaryMessengerHandler key values are the name of the channel

  • Codec (Codec) : In a channel, the data carried by Messenger needs to be transferred between the Dart and Native layers, so a platform-independent data protocol is required to support resources such as images and files. Therefore, binary byte stream is officially adopted as the data transmission protocol. Binary byte stream: The sender needs to encode data into binary data, and the receiver decodes the data into raw data. Codec is responsible for encoding and decoding the data. There are two kinds of Codec in flutter

FlutterMessageCodec

FlutterMessageCodec: the encoding and decoding of message between binary and underlying data. FlutterBasicMessenger uses this Codec. MessageCodec has multiple implementations in flutter

@protocol FlutterMessageCodec
+ (instancetype)sharedInstance;
// Encode the specified type of message as binary data
- (NSData* _Nullable)encode:(id _Nullable)message;
// Decodes the binary NSData to the specified type
- (id _Nullable)decode:(NSData* _Nullable)message;
@end
Copy the code

  • FlutterStandardMessageCodecIs:BasicMessage ChannelThe default codec is used, and the underlying codec is usedFlutterStandardReaderWriterImplemented for codec between data type and binary. Support basic data types include (bool/char/string/double/float/int/long/short/array/dictionary) and binary data
  • FlutterBinaryCodec: Used for encoding and decoding between binary data and binary data, in the implementation of only the received binary data will be returned intact
  • FlutterStringCodec: Used to encode and decode between string and binary datautf-8Coding format
  • FlutterJSONMessageCodec: Codec between data types and binary data. Supports basic data types and can be used on iOSNSJSONSerializationAs a serialization tool

FlutterMethodCodec

  • FlutterMethodCodecUsed for binary data and method callsFlutterMethodCallAnd return result codec, mainly used inFlutterMethodChannelandFlutterEventChannelIn the
@protocol FlutterMethodCodec
+ (instancetype)sharedInstance;
// Encode FlutterMethodCall as binary NSData
- (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall;
// Decode binary NSData into FlutterMethodCall
- (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall;
// Encode the normal response result as binary
- (NSData*)encodeSuccessEnvelope:(id _Nullable)result;
// Encode the error response prompt as binary NSData
- (NSData*)encodeErrorEnvelope:(FlutterError*)error;
// Decodes binary NSData. FlutterError is returned on failure
- (id _Nullable)decodeEnvelope:(NSData*)envelope;
@end
Copy the code

FlutterMethodCall represents method calls made from the Flutter side. Method calls include the method name, parameters, and return result. So compared to FlutterMessageCodec, there are two more methods in FlutterMethodCodec that handle the result of the call. There are currently two implementations in FlutterMethodCodec

  • FlutterJSONMethodCodec: in theFlutterMethodCallObject is first converted to a JSON object when encoded
@interface FlutterJSONMethodCodec : NSObject <FlutterMethodCodec>
Copy the code
- (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
  return [[FlutterJSONMessageCodec sharedInstance] encode:@{
    @"method" : call.method,
    @"args" : [self wrapNil:call.arguments],
  }];
}
Copy the code

When encoding the result of the call, it is converted to an array of [result] for success and [code, message, details] for failure.

- (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],
  ]];
}
Copy the code
  • FlutterStandardMethodCodecIs:FlutterMethodCodecThe default implementation when it is encoded in theFlutterMethodCallWhen the object is encoded, theMethodandargsIn turn useFlutterStandardReaderWriterEncode it and write it as binary data.

Delve into codec principles

We have said above the FlutterStandardReaderWriter FlutterStandardMessageCode and FlutterStandardMethodCodec are used for encoding, Then we’ll look at the definition of FlutterStandardMethodCodec

+ (instancetype)sharedInstance {
  static id _sharedInstance = nil;
  if(! _sharedInstance) { FlutterStandardReaderWriter* readerWriter = [[[FlutterStandardReaderWriter alloc] init] autorelease];  _sharedInstance = [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter]; }return _sharedInstance;
}
Copy the code

Key to decoding analysis 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 enumerated values to identify different data types, 0 for null and 3 for Int32
// When writing data of the specified type to NSData, the flag bit is written first, followed by the value
// When reading data from NSData, the type flag is read first and then the specific value is read
typedef NS_ENUM(NSInteger, FlutterStandardField) {
  FlutterStandardFieldNil,
  FlutterStandardFieldTrue,
  FlutterStandardFieldFalse,
  FlutterStandardFieldInt32,
  FlutterStandardFieldInt64,
  FlutterStandardFieldIntHex,
  FlutterStandardFieldFloat64,
  FlutterStandardFieldString,
  FlutterStandardFieldUInt8Data,
  FlutterStandardFieldInt32Data,
  FlutterStandardFieldInt64Data,
  FlutterStandardFieldFloat64Data,
  FlutterStandardFieldList,
  FlutterStandardFieldMap,
  FlutterStandardFieldFloat32Data,
};
Copy the code

ReaderWithData: Reads a value from NSData writerWithData: writes a value to NSData

HandlerMessage processing

Flutter in defines a handler is used to process after the Codec decoding of messages, in the use of the channel, the need to set the corresponding handler, essentially for its registered a corresponding FlutterBinaryMessageHandler, Binary data is FlutterBinaryMessageHandler for processing, the first to use the Codec decoding operation, and then distributed to specific Handler for processing. Corresponding to the three Platform channels, three types of handlers are defined in Flutter

  • FlutterMessageHandlerUsed to process strings or semi-structured messages
typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback);
Copy the code
  • FlutterMethodCallHandlerUsed to handle method calls
  • FlutterStreamHandlerUsed for event flow messages, usually for platforms to actively send event notifications to Flutter
@protocol FlutterStreamHandler
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
                                       eventSink:(FlutterEventSink)events;
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments;
@end
Copy the code

OnListenWithArguments: eventSink: when to start listening to Flutter end platform event, to the platform to launch a MethodCall method called listen, namely will eventually call FlutterStreamHandler in this method, The eventSink parameter can be used to send event onCancelWithArguments to a Flutter: When the Flutter end stops listening for platform events, A MethodCall is issued to the platform with a method named Cancel, which ultimately calls the method in FlutterStreamHandler, where it is common to destroy unwanted resources