美文网首页Flutter
Flutter Channel通信原理(上)

Flutter Channel通信原理(上)

作者: bigParis | 来源:发表于2020-05-14 22:07 被阅读0次

    当我们尝试理解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设计与实现(一)

    相关文章

      网友评论

        本文标题:Flutter Channel通信原理(上)

        本文链接:https://www.haomeiwen.com/subject/ysmxohtx.html