Flutter Channel通信原理(上)

当我们尝试理解dart是如何调用原生代码的时候,80%的人都能说出来是根据channel,但再问为什么channel就可以调到原生的代码了呢,能说上来的人就不足1%了,本文旨在刨根问题,有些内容我现在也不理解为什么是这样的。

dart调用原生方法

当我们发生一次dart调用原生方法的时候,使用VSCode或者AndroidStudio调试dart代码都会有类似的dart层堆栈,这里基本上是dart层所有的调用了, 我们看具体的代码

amap_core.dart
class AmapCore {
  static Future<void> init(String iosKey) {
    return platform(
      android: (pool) async {
        // 高德的android SDK没有提供一个公共的library, 所以无法在amap_core中提供一个公共
        // 的初始化方法.
        // 0.17.0开始的amap_map_fluttify提供了一个通过代码初始的方法AmapService.init(iosKey:androidKey:);
      },
      ios: (pool) async {
        final service = await AMapServices.sharedServices();
        await service.set_apiKey(iosKey);
        await service.set_enableHTTPS(true);
      },
    );
  }
}
AMapServices.g.dart

Future<void> set_enableHTTPS(bool enableHTTPS) async {
    await MethodChannel('me.yohom/amap_core_fluttify').invokeMethod('AMapServices::set_enableHTTPS', {'refId': refId, "enableHTTPS": enableHTTPS});
  }
  platform_channel.dart

  Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
    assert(method != null);
    final ByteData result = await binaryMessenger.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    final T typedResult = codec.decodeEnvelope(result);
    return typedResult;
  }
  bindding.dart

  @override
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if (handler != null)
      return handler(message);
    return _sendPlatformMessage(channel, message);
  }

  Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();
    // ui.window is accessed directly instead of using ServicesBinding.instance.window
    // because this method might be invoked before any binding is initialized.
    // This issue was reported in #27541. It is not ideal to statically access
    // ui.window because the Window may be dependency injected elsewhere with
    // a different instance. However, static access at this location seems to be
    // the least bad option.
    ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('during a platform message response callback'),
        ));
      }
    });
    return completer.future;
  }

来到关键点,platform_channel.dart 文件中: invokeMethod方法,这是所有dart代码调用原生方法的入口了,invokeMethod接受2个参数,methodarguments,在这里会调用binaryMessenger.send发送消息,传递了2个变量namecodec.encodeMethodCall(MethodCall(method, arguments)),下面先来来具体解释binaryMessengername,
在platform_channel.dart中会有如下代码

class MethodChannel {
  const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ])
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MethodCodec codec;
  BinaryMessenger get binaryMessenger => _binaryMessenger ?? defaultBinaryMessenger;
  // 以下省略
}

name作为MethodChannel的属性在构造函数中被初始化,再来看调用的地方

await MethodChannel('me.yohom/amap_core_fluttify')

这里只传递了一个参数,所以其它两个参数都是取了默认值,binaryMessenger是一个get方法,当_binaryMessenger属性值为null的时候取的是defaultBinaryMessenger, defaultBinaryMessenger也是一个get方法,具体的代码在binding.dart中,用法和binaryMessenger类似,最终binaryMessenger是一个_DefaultBinaryMessenger类的实例,这个可以在binding.dart的代码中找到最终的答案,这里针对binaryMessenger就不做更多的介绍了。

下面来具体解释第二个参数:codec.encodeMethodCall(MethodCall(method, arguments))
这里首先用到了MethodChannel构造的时候第二个参数codec,默认值是StandardMethodCodec,再看encodeMethodCall方法的定义:

message_codec.dart
abstract class MethodCodec {
  /// Encodes the specified [methodCall] into binary.
  ByteData encodeMethodCall(MethodCall methodCall);

  /// Decodes the specified [methodCall] from binary.
  MethodCall decodeMethodCall(ByteData methodCall);

  /// Decodes the specified result [envelope] from binary.
  ///
  /// Throws [PlatformException], if [envelope] represents an error, otherwise
  /// returns the enveloped result.
  dynamic decodeEnvelope(ByteData envelope);

  /// Encodes a successful [result] into a binary envelope.
  ByteData encodeSuccessEnvelope(dynamic result);

  /// Encodes an error result into a binary envelope.
  ///
  /// The specified error [code], human-readable error [message], and error
  /// [details] correspond to the fields of [PlatformException].
  ByteData encodeErrorEnvelope({ @required String code, String message, dynamic details });
}

这里MethodCodec类是一个抽象类,StandardMethodCodecMethodCodec一个子类,负责具体的实现

message_codecs.dart
class StandardMethodCodec implements MethodCodec {
  // The codec method calls, and result envelopes as outlined below. This format
  // must match the Android and iOS counterparts.
  //
  // * Individual values are encoded using [StandardMessageCodec].
  // * Method calls are encoded using the concatenation of the encoding
  //   of the method name String and the arguments value.
  // * Reply envelopes are encoded using first a single byte to distinguish the
  //   success case (0) from the error case (1). Then follows:
  //   * In the success case, the encoding of the result value.
  //   * In the error case, the concatenation of the encoding of the error code
  //     string, the error message string, and the error details value.

  /// Creates a [MethodCodec] using the Flutter standard binary encoding.
  const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]);

  /// The message codec that this method codec uses for encoding values.
  final StandardMessageCodec messageCodec;

  @override
  ByteData encodeMethodCall(MethodCall call) {
    final WriteBuffer buffer = WriteBuffer();
    messageCodec.writeValue(buffer, call.method);
    messageCodec.writeValue(buffer, call.arguments);
    return buffer.done();
  }

  @override
  MethodCall decodeMethodCall(ByteData methodCall) {
    final ReadBuffer buffer = ReadBuffer(methodCall);
    final dynamic method = messageCodec.readValue(buffer);
    final dynamic arguments = messageCodec.readValue(buffer);
    if (method is String && !buffer.hasRemaining)
      return MethodCall(method, arguments);
    else
      throw const FormatException('Invalid method call');
  }

子类实现了抽象类中定义的接口encodeMethodCall,这里用到WriteBuffer类将传入的参数MethodCall转换成ByteData, 这里先不详细展开讲WriteBuffer,我们再说下encodeMethodCall的参数MethodCall

message_codec.dart
class MethodCall {
  /// Creates a [MethodCall] representing the invocation of [method] with the
  /// specified [arguments].
  const MethodCall(this.method, [this.arguments])
    : assert(method != null);

  /// The name of the method to be called.
  final String method;

  /// The arguments for the method.
  ///
  /// Must be a valid value for the [MethodCodec] used.
  final dynamic arguments;

  @override
  String toString() => '$runtimeType($method, $arguments)';
}

这里实际是对方法进行了dart层面的封装,将其转换成对象,保存了方法名methodarguments,后面这2个参数会被取出来,通过messageCodec.writeValue(buffer, xxx)方法写入到WriteBuffer中。

至此,应该已经解释清楚了binaryMessenger实例和name参数

await binaryMessenger.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );

上面这段代码的调用有2个参数了,下面来详细解释调用binaryMessenger.send, 由于binaryMessenger最终是_DefaultBinaryMessenger, 所以实际调用的是_DefaultBinaryMessengersend方法

  binding.dart

  @override
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if (handler != null)
      return handler(message);
    return _sendPlatformMessage(channel, message);
  }

这里在没有设置_mockHandlers的时候,会执行_sendPlatformMessage私有方法,关于_mockHandlers后面再展开分析

    binding.dart
    Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();
    // ui.window is accessed directly instead of using ServicesBinding.instance.window
    // because this method might be invoked before any binding is initialized.
    // This issue was reported in #27541. It is not ideal to statically access
    // ui.window because the Window may be dependency injected elsewhere with
    // a different instance. However, static access at this location seems to be
    // the least bad option.
    ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('during a platform message response callback'),
        ));
      }
    });
    return completer.future;
  }

上面的这段代码已经是调试的尽头了,最终调用ui.window.sendPlatformMessage将参数传递给ui.windowsendPlatformMessage方法,并设置了reply,但是,还远远没有结束,我们来看下window.dart的源码

  window.dart
  void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) {
    final String error =
        _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
    if (error != null)
      throw Exception(error);
  }
  String _sendPlatformMessage(String name,
                              PlatformMessageResponseCallback callback,
                              ByteData data) native 'Window_sendPlatformMessage';

这里是dart代码的执行尽头,sendPlatformMessage将传入的callback进行包装

  window.dart
  /// Wraps the given [callback] in another callback that ensures that the
  /// original callback is called in the zone it was registered in.
  static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
    if (callback == null)
      return null;

    // Store the zone in which the callback is being registered.
    final Zone registrationZone = Zone.current;

    return (ByteData data) {
      registrationZone.runUnaryGuarded(callback, data);
    };
  }

官方已经细心的给了解释,是为了让callback在执行的时候能够和它注册的时候在一个zone关于Zone,由于这里不是本文要阐述的核心路径,所以不展开讲sendPlatformMessage->_sendPlatformMessage结束了dart代码,转为Native层,这里所说的Native并不是Java、OC原生代码,而是Flutter引擎自带的Native层代码,由c++编写,下面的代码也都是开源的,但我们跟代码已经无法跟到了,需要手动下载源码或在sourcegraph进行源码的阅读。这里用到了native这个关键字

String _sendPlatformMessage(String name,
                              PlatformMessageResponseCallback callback,
                              ByteData data) native 'Window_sendPlatformMessage'

意思是,这个接口是由native方法Window_sendPlatformMessage导出成、dart语言的,在dart中,方法名是_sendPlatformMessage关于为什么在dart中使用native关键字就可以定义出native方法的导出,后面再研究,最终我们找到c++的源码

window.cc
void _SendPlatformMessage(Dart_NativeArguments args) {
  tonic::DartCallStatic(&SendPlatformMessage, args);
}

Dart_Handle SendPlatformMessage(Dart_Handle window,
                                const std::string& name,
                                Dart_Handle callback,
                                Dart_Handle data_handle) {
  UIDartState* dart_state = UIDartState::Current();

  if (!dart_state->window()) {
    return tonic::ToDart(
        "Platform messages can only be sent from the main isolate");
  }

  fml::RefPtr<PlatformMessageResponse> response;
  if (!Dart_IsNull(callback)) {
    response = fml::MakeRefCounted<PlatformMessageResponseDart>(
        tonic::DartPersistentValue(dart_state, callback),
        dart_state->GetTaskRunners().GetUITaskRunner());
  }
  if (Dart_IsNull(data_handle)) {
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(name, response));
  } else {
    tonic::DartByteData data(data_handle);
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(
            name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
            response));
  }

  return Dart_Null();
}

dart_args.h

void DartCallStatic(Sig func, Dart_NativeArguments args) {
  DartArgIterator it(args, 0);
  using Indices = typename IndicesForSignature<Sig>::type;
  DartDispatcher<Indices, Sig> decoder(&it);
  if (it.had_exception())
    return;
  decoder.Dispatch(func);
}

template <size_t... indices, typename... ArgTypes>
struct DartDispatcher<IndicesHolder<indices...>, void (*)(ArgTypes...)>
    : public DartArgHolder<indices, ArgTypes>... {
  using FunctionPtr = void (*)(ArgTypes...);

  DartArgIterator* it_;

  explicit DartDispatcher(DartArgIterator* it)
      : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {}

  void Dispatch(FunctionPtr func) {
    (*func)(DartArgHolder<indices, ArgTypes>::value...);
  }
};

上面的2段代码,第一段还好,第二段就非常晦涩了,DartDispatcher这个结构体的定义就有好几种,我们先姑且认为这段代码是用来做消息分发的,并且将参数列表args数组中的对象传递给对应的函数,最终调用到了SendPlatformMessage这个方法,这里会根据callbackdata_handle是否为空进行特殊的处理,大部分情况会走到dart_state->window()->client()->HandlePlatformMessage, 这里首先会把data_handle转换成data, 然后写到内存buffer,然后调用HandlePlatformMessage, 一路跟踪会看到client实际是WindowClient对象

class WindowClient {
 public:
  virtual std::string DefaultRouteName() = 0;
  virtual void ScheduleFrame() = 0;
  virtual void Render(Scene* scene) = 0;
  virtual void UpdateSemantics(SemanticsUpdate* update) = 0;
  virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0;
  virtual FontCollection& GetFontCollection() = 0;
  virtual void UpdateIsolateDescription(const std::string isolate_name,
                                        int64_t isolate_port) = 0;
  virtual void SetNeedsReportTimings(bool value) = 0;
  virtual std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() = 0;

 protected:
  virtual ~WindowClient();
};

HandlePlatformMessage是抽象方法,真实的定义不在Window.cc中,而在Runtime_controller

runtime_controller.h
class RuntimeController final : public WindowClient {
 public:
  RuntimeController(
      RuntimeDelegate& client,
      DartVM* vm,
      fml::RefPtr<const DartSnapshot> isolate_snapshot,
      TaskRunners task_runners,
      fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
      fml::WeakPtr<IOManager> io_manager,
      fml::RefPtr<SkiaUnrefQueue> unref_queue,
      fml::WeakPtr<ImageDecoder> image_decoder,
      std::string advisory_script_uri,
      std::string advisory_script_entrypoint,
      const std::function<void(int64_t)>& idle_notification_callback,
      const WindowData& window_data,
      const fml::closure& isolate_create_callback,
      const fml::closure& isolate_shutdown_callback,
      std::shared_ptr<const fml::Mapping> persistent_isolate_data);

  // |WindowClient|
  ~RuntimeController() override;

  //----------------------------------------------------------------------------
  /// @brief      Clone the the runtime controller. This re-creates the root
  ///             isolate with the same snapshots and copies all window data to
  ///             the new instance. This is usually only used in the debug
  ///             runtime mode to support the cold-restart scenario.
  ///
  /// @return     A clone of the existing runtime controller.
  ///
  std::unique_ptr<RuntimeController> Clone() const;
  
以下省略
}

runtime_controller.cc
// |WindowClient|
void RuntimeController::HandlePlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  client_.HandlePlatformMessage(std::move(message));
}

client_RuntimeDelegate的引用,所以要看谁继承或者就是RuntimeDelegate

shell/common/engine

class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
 public:
 private:
  Engine::Delegate& delegate_;
}

可见,Engine正是我们要寻找的RuntimeDelegate, 在engine.cc中我们找到了HandlePlatformMessage

engine.h
void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) {
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else {
    delegate_.OnEngineHandlePlatformMessage(std::move(message));
  }
}

class Delegate {
   public:
virtual void OnEngineHandlePlatformMessage(
        fml::RefPtr<PlatformMessage> message) = 0;
 以下省略
}

这里engine::Delegate也是一个抽象类,还要找它的实现类

shell.h
class Shell final : public PlatformView::Delegate,
                    public Animator::Delegate,
                    public Engine::Delegate,
                    public Rasterizer::Delegate,
                    public ServiceProtocol::Handler {
}

shell是delegate的一个实现

shell.cc
// |Engine::Delegate|
void Shell::OnEngineHandlePlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  FML_DCHECK(is_setup_);
  FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());

  if (message->channel() == kSkiaChannel) {
    HandleEngineSkiaMessage(std::move(message));
    return;
  }

  task_runners_.GetPlatformTaskRunner()->PostTask(
      [view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
        if (view) {
          view->HandlePlatformMessage(std::move(message));
        }
      });
}

我们看到上面的代码还是很人性化的,Shell作为Engine::Delegate的实现,还是有所标注的,这里会根据message->channel是否是kSkiaChannel进行不同的处理,kSkiaChannel具体做了哪些事情后面再研究。正常的路径,用户自定义的channel将会调用
view->HandlePlatformMessage(std::move(message)), 这里platform_view_我们先简单理解为实际对应平台的view。关于上述一系列C++抽象类是怎么找到最终具体的实现类的,我们再另一篇博文详细分析

PlatformViewIOS完整代码

// |PlatformView|
void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr<flutter::PlatformMessage> message) {
  platform_message_router_.HandlePlatformMessage(std::move(message));
}

PlatformViewAndroid完整代码

// |PlatformView|
void PlatformViewAndroid::HandlePlatformMessage(
    fml::RefPtr<flutter::PlatformMessage> message) {
  JNIEnv* env = fml::jni::AttachCurrentThread();
  fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
  if (view.is_null())
    return;

  int response_id = 0;
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    pending_responses_[response_id] = response;
  }
  auto java_channel = fml::jni::StringToJavaString(env, message->channel());
  if (message->hasData()) {
    fml::jni::ScopedJavaLocalRef<jbyteArray> message_array(
        env, env->NewByteArray(message->data().size()));
    env->SetByteArrayRegion(
        message_array.obj(), 0, message->data().size(),
        reinterpret_cast<const jbyte*>(message->data().data()));
    message = nullptr;

    // This call can re-enter in InvokePlatformMessageXxxResponseCallback.
    FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
                                     message_array.obj(), response_id);
  } else {
    message = nullptr;

    // This call can re-enter in InvokePlatformMessageXxxResponseCallback.
    FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
                                     nullptr, response_id);
  }
}

现在可以确定PlatformViewAndroid对应的应该是Android平台的,PlatformViewIOS对应的是iOS的,以iOS为例,这里PlatformViewIOS继续分发,把HandlePlatformMessageplatform_message_router_来处理,

PlatformMessageRouter.h完整代码

namespace flutter {

class PlatformMessageRouter {
 public:
  PlatformMessageRouter();
  ~PlatformMessageRouter();

  void HandlePlatformMessage(
      fml::RefPtr<flutter::PlatformMessage> message) const;

  void SetMessageHandler(const std::string& channel,
                         FlutterBinaryMessageHandler handler);

 private:
  std::unordered_map<std::string, fml::ScopedBlock<FlutterBinaryMessageHandler>>
      message_handlers_;

  FML_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter);
};

}  

PlatformMessageRouter.mm完整代码

void PlatformMessageRouter::HandlePlatformMessage(
    fml::RefPtr<flutter::PlatformMessage> message) const {
  fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response();
  auto it = message_handlers_.find(message->channel());
  if (it != message_handlers_.end()) {
    FlutterBinaryMessageHandler handler = it->second;
    NSData* data = nil;
    if (message->hasData()) {
      data = GetNSDataFromVector(message->data());
    }
    handler(data, ^(NSData* reply) {
      if (completer) {
        if (reply) {
          completer->Complete(GetMappingFromNSData(reply));
        } else {
          completer->CompleteEmpty();
        }
      }
    });
  } else {
    if (completer) {
      completer->CompleteEmpty();
    }
  }
}

终于看到了我们iOS开发熟悉的mm文件了,从message_handlers_里找到存储的channel对应的handler, 然后如果有数据就把数据转成NSData,最后执行handler,这里handler已经是OC代码定义的一个block了,block执行的时候会传递2个参数,一个是message,这个message自从执行到C++代码后就封装成了fml::RefPtr<PlatformMessage>类型的指针,期间Native层面一直未曾改变,第二个参数FlutterBinaryReply也是一个block,当completer存在就会调用completer->Complete或者completer->CompleteEmpty(),用于接收原生代码的回调,有关原生代码如果回调或者调用dart代码,会在另外一篇博文分析

FlutterBinaryMessenger.h
typedef void (^FlutterBinaryMessageHandler)(NSData* _Nullable message, FlutterBinaryReply reply);

typedef void (^FlutterBinaryReply)(NSData* _Nullable reply);

到这里就已经完成了Native层的调用,可以看到,里面的逻辑还是非常复杂的,最终原生代码又是如何被执行的,我们需要找到传递或者存储message_handlers_的地方。

PlatformMessageRouter.mm

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};
  }
}

这里SetMessageHandler定义的地方在FlutterBasicMessageChannel类,我们直接可以在iOS工程中找到这个类的头文件,文件名是FlutterChannels.h,所以我们去找对应的源码

FlutterChannels.mm
@implementation FlutterBasicMessageChannel {
  NSObject<FlutterBinaryMessenger>* _messenger;
  NSString* _name;
  NSObject<FlutterMessageCodec>* _codec;
}

- (void)setMessageHandler:(FlutterMessageHandler)handler {
  if (!handler) {
    [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
    return;
  }
  // Grab reference to avoid retain on self.
  NSObject<FlutterMessageCodec>* codec = _codec;
  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
    handler([codec decode:message], ^(id reply) {
      callback([codec encode:reply]);
    });
  };
  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}

这里在FlutterMessageHandler不为空的时候,会调用FlutterBinaryMessenger类的setMessageHandlerOnChannel方法,继续找FlutterBinaryMessenger.mm, 没有找到这个文件,因为我忽略了,_messenger是一个遵守FlutterBinaryMessenger协议的对象,而不是FlutterBinaryMessenger类的对象,所以我们只要找到是哪个类实现了FlutterBinaryMessenger中的setMessageHandlerOnChannel接口就可以了。

我们发现大多数时候messenger都是作为参数进行传递的,只有FlutterPluginRegistrar这个协议里定义了这个接口

@protocol FlutterPluginRegistrar <NSObject>
/**
 * Returns a `FlutterBinaryMessenger` for creating Dart/iOS communication
 * channels to be used by the plugin.
 *
 * @return The messenger.
 */
- (NSObject<FlutterBinaryMessenger>*)messenger;
以下省略
}

同理,registrar也是通过实现接口registerWithRegistrar拿到的

@protocol FlutterPlugin <NSObject, FlutterApplicationLifeCycleDelegate>
@required
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
以下省略
@end

我们所有定义的插件都是要遵守FlutterPlugin协议的,例如JPushPlugin

@interface JPushPlugin : NSObject<FlutterPlugin>
@property FlutterMethodChannel *channel;
@end

这样每个组件通过实现registerWithRegistrar拿到FlutterPluginRegistrar进而拿到FlutterBinaryMessenger对象,那到底谁调用了registerWithRegistrar呢?

@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
GeneratedPluginRegistrant.m
#if __has_include(<jpush_flutter/JPushPlugin.h>)
#import <jpush_flutter/JPushPlugin.h>
#else
@import jpush_flutter;
#endif
@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[JPushPlugin registerWithRegistrar:[registry registrarForPlugin:@"JPushPlugin"]];
}

这次应该算是抛到祖坟了,didFinishLaunchingWithOptions是iOS App启动后执行的第一个方法,一般初始化操作会放在这里进行。GeneratedPluginRegistrant应该是一个插件初始化的管理类,这里的代码都是自动生成的,有关代码自动生成的实现原理后面分析registry就是AppDelegate,FlutterApp的AppDelegate继承自FlutterAppDelegate

@interface FlutterAppDelegate
    : UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>

FlutterAppDelegate类或其子类需要实现FlutterPluginRegistry协议的registrarForPlugin接口
FlutterAppDelegate.mm

- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
  UIViewController* rootViewController = _window.rootViewController;
  if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
    return
        [[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
  }
  return nil;
}

所以有了这个实现就不难解释GeneratedPluginRegistrant类中,[registry registrarForPlugin:@"JPushPlugin"]实际调用的是上面的这段代码,FlutterViewController源码pluginRegistry实际返回的是一个遵守FlutterPluginRegistry协议的_engine对象

@implementation FlutterViewController {
  std::unique_ptr<fml::WeakPtrFactory<FlutterViewController>> _weakFactory;
  fml::scoped_nsobject<FlutterEngine> _engine;

  // We keep a separate reference to this and create it ahead of time because we want to be able to
  // setup a shell along with its platform view before the view has to appear.
  fml::scoped_nsobject<FlutterView> _flutterView;
  fml::scoped_nsobject<UIView> _splashScreenView;
  fml::ScopedBlock<void (^)(void)> _flutterViewRenderedCallback;
  UIInterfaceOrientationMask _orientationPreferences;
  UIStatusBarStyle _statusBarStyle;
  flutter::ViewportMetrics _viewportMetrics;
  BOOL _initialized;
  BOOL _viewOpaque;
  BOOL _engineNeedsLaunch;
  NSMutableSet<NSNumber*>* _ongoingTouches;
  // This scroll view is a workaround to accomodate iOS 13 and higher.  There isn't a way to get
  // touches on the status bar to trigger scrolling to the top of a scroll view.  We place a
  // UIScrollView with height zero and a content offset so we can get those events. See also:
  // https://github.com/flutter/flutter/issues/35050
  fml::scoped_nsobject<UIScrollView> _scrollView;
}
...省略若干行
- (id<FlutterPluginRegistry>)pluginRegistry {
  return _engine;
}

其实[(FlutterViewController*)rootViewController pluginRegistry]就是获取FlutterEngine对象了, Engine


- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
  NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
  self.pluginPublications[pluginKey] = [NSNull null];
  FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
                                                                    flutterEngine:self];
  self.registrars[pluginKey] = result;
  return [result autorelease];
}

实际是把,pluginKeyself传给FlutterEngineRegistrar并获取了FlutterEngineRegistrar类的对象,然后用字典self.registrars保存pluginKeyresult,这里应该是引擎的核心代码了,主要是管理插件。FlutterEngineRegistrar就是我们代码里用到的registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry中的registry, FlutterEngineRegistrar类的源码也在Engine

@implementation FlutterEngineRegistrar {
  NSString* _pluginKey;
}

- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
  self = [super init];
  NSAssert(self, @"Super init cannot be nil");
  _pluginKey = [pluginKey copy];
  _flutterEngine = flutterEngine;
  return self;
}

- (NSObject<FlutterBinaryMessenger>*)messenger {
  return _flutterEngine.binaryMessenger;
}

初始化就是把传进来的pluginKeyflutterEngine存起来了,并且在FlutterEngineRegistrar中,终于找到了我们寻觅已久的messenger

@implementation FlutterEngine {
  fml::scoped_nsobject<FlutterDartProject> _dartProject;
  flutter::ThreadHost _threadHost;
  std::unique_ptr<flutter::Shell> _shell;
  NSString* _labelPrefix;
  std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;

  fml::WeakPtr<FlutterViewController> _viewController;
  fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher;

  std::unique_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
  std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
  std::unique_ptr<flutter::SamplingProfiler> _profiler;

  // Channels
  fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
  fml::scoped_nsobject<FlutterTextInputPlugin> _textInputPlugin;
  fml::scoped_nsobject<FlutterMethodChannel> _localizationChannel;
  fml::scoped_nsobject<FlutterMethodChannel> _navigationChannel;
  fml::scoped_nsobject<FlutterMethodChannel> _platformChannel;
  fml::scoped_nsobject<FlutterMethodChannel> _platformViewsChannel;
  fml::scoped_nsobject<FlutterMethodChannel> _textInputChannel;
  fml::scoped_nsobject<FlutterBasicMessageChannel> _lifecycleChannel;
  fml::scoped_nsobject<FlutterBasicMessageChannel> _systemChannel;
  fml::scoped_nsobject<FlutterBasicMessageChannel> _settingsChannel;

  int64_t _nextTextureId;

  BOOL _allowHeadlessExecution;
  FlutterBinaryMessengerRelay* _binaryMessenger;
}

这个_binaryMessengerFlutterBinaryMessengerRelay类的实例, FlutterBinaryMessengerRelay.h中也可以看到,确实是遵守了FlutterBinaryMessenger协议,并且这里我们终于找到了setMessageHandlerOnChannel这个方法

- (void)setMessageHandlerOnChannel:(NSString*)channel
              binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
  if (self.parent) {
    [self.parent setMessageHandlerOnChannel:channel binaryMessageHandler:handler];
  } else {
    FML_LOG(WARNING) << "Communicating on a dead channel.";
  }
}

Engine


- (instancetype)initWithName:(NSString*)labelPrefix
                     project:(FlutterDartProject*)project
      allowHeadlessExecution:(BOOL)allowHeadlessExecution {
  self = [super init];
  NSAssert(self, @"Super init cannot be nil");
  NSAssert(labelPrefix, @"labelPrefix is required");

  _allowHeadlessExecution = allowHeadlessExecution;
  _labelPrefix = [labelPrefix copy];

  _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);

  if (project == nil)
    _dartProject.reset([[FlutterDartProject alloc] init]);
  else
    _dartProject.reset([project retain]);

  _pluginPublications = [NSMutableDictionary new];
  _registrars = [[NSMutableDictionary alloc] init];
  _platformViewsController.reset(new flutter::FlutterPlatformViewsController());

  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];

 这段是注册通知的代码,省略

  return self;
}

可以看到FlutterBinaryMessengerRelay里的parent就是FlutterEngine, 在 Engine中也确实有这个实现

- (void)setMessageHandlerOnChannel:(NSString*)channel
              binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
  NSParameterAssert(channel);
  NSAssert(_shell && _shell->IsSetup(),
           @"Setting a message handler before the FlutterEngine has been run.");
  self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
}

self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler)调用的就是下面的代码

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};
  }
}

好么,总算找到message_handlers_ 添加元素的地方了,整理一下调用堆栈

PlatformMessageRouter::SetMessageHandler
Engine::setMessageHandlerOnChannel
FlutterBinaryMessengerRelay::setMessageHandlerOnChannel
FlutterBasicMessageChannel::setMessageHandler

搜索工程的代码发现并没有调用FlutterBasicMessageChannel::setMessageHandler的地方,好吧,我们搞清了后面的流程,但是还没有人使用,这是我们从dart端开始分析,一直分析到原生代码得到的一条路径。

代码中更多的用法是这样的堆栈


image.png

大方向还是对的,问题出在我们刚才分析PlatformMessageRouter这个类的时候,被存储handlermessage_handlers_字典给影响了,我们根据实际的堆栈,找到FlutterChannels源码

- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
  if (!handler) {
    [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
    return;
  }
  // Make sure the block captures the codec, not self.
  NSObject<FlutterMethodCodec>* codec = _codec;
  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
    FlutterMethodCall* call = [codec decodeMethodCall:message];
    handler(call, ^(id result) {
      if (result == FlutterMethodNotImplemented)
        callback(nil);
      else if ([result isKindOfClass:[FlutterError class]])
        callback([codec encodeErrorEnvelope:(FlutterError*)result]);
      else
        callback([codec encodeSuccessEnvelope:result]);
    });
  };
  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}

原来,setMethodCallHandler里面也是调用setMessageHandlerOnChannel来进行注册的

PlatformMessageRouter::SetMessageHandler
Engine::setMessageHandlerOnChannel
FlutterBinaryMessengerRelay::setMessageHandlerOnChannel
FlutterMethodChannel::setMethodCallHandler

这样通过显示调用setMethodCallHandler就可以在回调里面进行dart代码调用的处理了,但是我们还发现更多的插件是像JPush这样

JPushPlugin.m
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    JPLog(@"handleMethodCall:%@",call.method);
    
    if ([@"getPlatformVersion" isEqualToString:call.method]) {
        result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
    } else if([@"setup" isEqualToString:call.method]) {
        [self setup:call result: result];
    } else if([@"applyPushAuthority" isEqualToString:call.method]) {
        [self applyPushAuthority:call result:result];
    } else if([@"setTags" isEqualToString:call.method]) {
        [self setTags:call result:result];
    }
}

后面省略类似的代码

JPushPlugin组件初始化的时候注册方法了方法回调addMethodCallDelegate

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    getRidResults = @[].mutableCopy;
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"jpush"
                                     binaryMessenger:[registrar messenger]];
    JPushPlugin* instance = [[JPushPlugin alloc] init];
    instance.channel = channel;
    
    
    [registrar addApplicationDelegate:instance];
    [registrar addMethodCallDelegate:instance channel:channel];
}

经过上面的分析,我们不难找到源码还是在FlutterEngine.mm

- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
                      channel:(FlutterMethodChannel*)channel {
  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
    [delegate handleMethodCall:call result:result];
  }];
}

这样原生代码通过channel名来注册methodCallHandler, 最终handler存储到PlatformMessageRoutermessage_handlers_中,当有dart通过invokeMethod方法调用原生方法的时候,就会跟进channel取出对应的handler,然后执行handler,使原生代码setMethodCallHandler的回调执行,然后这里又使用Plugin传进来的delegate将调用信息回调到handleMethodCall方法上,由于delegate是遵守FlutterPlugin协议的,所以只要在有类实现FlutterPlugin- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result方法就可以处理来自dart的方法调用了。

总结

dart代码调用原生接口,经历了两个阶段:

  • dart封装层的转发,最终来到ui.window.sendPlatformMessage
  • Native层的转发,最终会根据不同平台调用不同的方法,iOS: HandlePlatformMessage, Android: FlutterViewHandlePlatformMessage

原生代码能够正确接收到消息,是通过注册handler的方式:

  • Plugin通过addMethodCallDelegatesetMethodCallHandler注册
  • FlutterChannel通过setMethodCallHandler进行注册
  • Engine通过setMessageHandlerOnChannel注册
  • PlatformMessageRouter通过SetMessageHandler注册,存在message_handlers_

遗留问题

  • StandardMethodCodec中用到的WriteBuffer实现细节
  • _DefaultBinaryMessenger中用到_mockHandlers实现细节
  • window.dart中使用了ZoneZone如何保证注册和执行的时候是同一个zone
  • 为什么在dart中使用native关键字就可以定义出C++方法的导出
  • DartDispatcher是如何分发消息最终调用到SendPlatformMessage
  • channelkSkiaChannel的时候,后面具体做了哪些事情
  • 一系列C++抽象类是怎么找到最终具体的实现类的
  • 原生代码如果回调或者调用dart代码,会在另外一篇博文分析
  • GeneratedPluginRegistrant自动生成代码原理

参考文献

深入Flutter技术内幕:Platform Channel设计与实现(一)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342