flutter 消息传递机制

flutter 消息传递机制

flutter 中的 WidgetsFlutterBinding 集成了 GestureBinding、ServicesBinding、SchedulerBinding、PaintingBinding、SemanticsBinding、RendererBinding、WidgetsBinding 等 7 种 Binding,它们都有本身在功能上的划分,其中,ServicesBinding 主要负责的是 flutter 与 native 之间传递信息相关。java

在 ServicesBinding 的 initInstances 函数中主要有两个操做,将 window.onPlatformMessage 与 defaultBinaryMessenger.handlePlatformMessage 关联起来,再读取 license 相关的信息,从使用上来讲,license 主要是为了支撑 about 页面的展现,除此以外好像没有其余的用处。android

而 defaultBinaryMessenger 就是 flutter 与 native 之间进行消息传递的关键,它负责在两者之间提供接收、发送数据的接口。首先,从接口上看,它有提供 send、setMessageHandler 这两个函数,分别用于发送数据、处理数据,flutter 上层的 BasicMessageChannel、MethodChannel、EventChannel 等都是基于它实现的。从 flutter 层来看,它的消息传递机制包括三层:ios

  1. 基础传输:主要实现位于 engine 中,内部不区分消息类型
  2. 分类传输:主要实现位于 flutter 中,将消息分为三种类型,BasicMessageChannel、MethodChannel 和 EventChannel,它们对数据传输作了封装,分别支持跨层的单次数据传递(含接收数据)、方法调用(含调用和被调用)和持续数据接收。
  3. 上层调用:这部分主要是一些基础数据传输实现的功能,好比 SystemChannels.navigation、SystemChannels.system 和 flutter 插件的实现等。

如下分层了解:c++

基础传输

这部分以 BinaryMessenger 为主体,看一下它的各个函数是如何实现的,实现主要分为两部分:发送消息和接收消息。发送消息包括两部分,首先,flutter 将一个数据经过 engine 层传递到 native 层,native 处理完了以后会有一个结果返回的过程,表现出来就是回调,这也正是 BinaryMessenger 的 send 函数返回结果是一个 Future 类型的缘由。而把上面这个过程反过来,改成 native 向 flutter 发送消息,flutter 回应,那么回应的这部分就对应着接收消息的实现,处理结果经过 callback 回调给 native 。因此 flutter 中的发送、接收消息能够看做是一个常规消息传递的一半,再结合 native 的发送、接收消息,就构成了两个发送、接收的流程。markdown

发送流程

发送过程从 send 开始,返回值是 Future,在 _sendPlatformMessage 中使用 Completer 将 Future 转成了回调函数:app

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 准备进入 engine 层,与之对应的函数为 SendPlatformMessage:async

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();
}
复制代码

在这里先是将 callback 封装成了 PlatformMessageResponseDart,方便以后的回调,而后再将 name 和 PlatformMessageResponseDart 一块儿封装成 PlatformMessage,做为单个消息发送。后经过 RuntimeController 转递给 Engine,在这里,Engine 对 asset 请求消息进行了拦截,HandleAssetPlatformMessage 对其负责。asset 请求通常用于 flutter 中获取 asset 文件夹的资源,这个由 engine 层直接处理,不须要经过 native 层。ide

其余的消息会再转递给 Shell,Shell 中又对 skia 消息进行拦截,能够设置 skia 的参数,目前只支持设置 SetResourceCacheMaxBytes,能够调用 flutter 中的 SystemChannels.skia 设置。函数

再剩下的消息就是须要 native 层处理的了,以 android 为例,ui

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);
  }
}
复制代码

这里会将 PlatformMessage 拆解,将 PlatformMessageResponseDart 保存在 pending_responses_ 中,将它的 id 传递到 native 中,供结果回调时使用,最后由 FlutterViewHandlePlatformMessage 经过 jni 调用 android 中的方法,对应 FlutterJNI#handlePlatformMessage。

public void handleMessageFromDart(
    @NonNull final String channel, @Nullable byte[] message, final int replyId) {
  Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
  BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
  if (handler != null) {
    try {
      Log.v(TAG, "Deferring to registered handler to process message.");
      final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
      handler.onMessage(buffer, new Reply(flutterJNI, replyId));
    } catch (Exception ex) {
      Log.e(TAG, "Uncaught exception in binary message listener", ex);
      flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
    }
  } else {
    Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
    flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
  }
}
复制代码

android 中也有一个相似于 defaultBinaryMessenger 的对象 platformMessageHandler,handleMessageFromDart 方法接收消息,并在其中从 messageHandlers 中取出对应 channel 的 handler(经过 setMessageHandler 设置),调用其 onMessage 方法进行处理,replyId 也就是以前生成的 responseId,它被封装成了 Reply。handler 是 BinaryMessageHandler 对象,在 android 中也有三种 Channel,BasicMessageChannel、MethodChannel 和 EventChannel(具体不一样类的处理过程,能够参照下面 flutter 中的介绍),其功用与 flutter 中对应类一致。

处理完以后则是调用 Reply 的 reply 方法,将结果回传给 flutter 。

public void reply(@Nullable ByteBuffer reply) {
  if (done.getAndSet(true)) {
    throw new IllegalStateException("Reply already submitted");
  }
  if (reply == null) {
    flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
  } else {
    flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
  }
}
复制代码

最上面是一个检验,一次来自 flutter 的消息,只能回传一个数据。而后根据结果是否为空选择调用 invokePlatformMessageEmptyResponseCallback 仍是 invokePlatformMessageResponseCallback,两者只是参数的区别。

接着 invokePlatformMessageResponseCallback 会经过 jni 调用 InvokePlatformMessageResponseCallback 函数,而后转到 PlatformViewAndroid::InvokePlatformMessageResponseCallback,以前在 flutter 发送消息时转成 responseId 的 PlatformMessageResponse 这里会被找回来,调用其 Complete 函数进行回调。

void PlatformMessageResponseDart::Complete(std::unique_ptr<fml::Mapping> data) {
  if (callback_.is_empty())
    return;
  FML_DCHECK(!is_complete_);
  is_complete_ = true;
  ui_task_runner_->PostTask(fml::MakeCopyable(
      [callback = std::move(callback_), data = std::move(data)]() mutable {
        std::shared_ptr<tonic::DartState> dart_state =
            callback.dart_state().lock();
        if (!dart_state)
          return;
        tonic::DartState::Scope scope(dart_state);

        Dart_Handle byte_buffer = WrapByteData(std::move(data));
        tonic::DartInvoke(callback.Release(), {byte_buffer});
      }));
}
复制代码

主要就是经过 DartInvoke 调用 flutter 中的 callback 函数,将结果 byte_buffer 传递回去,这个 callback,就是 _sendPlatformMessage 中声明的函数,complete 函数的入参。由此,Future 即可以获得结果,BinaryMessenger.send 函数返回的 Future 即可以获得结果,一次完整的消息发送过程结束。

接收流程

接收流程本质上就是发送流程的逆过程,可是细节上会有不一样,接收流程由 native 中的 send 函数开始,以 android 为例,就是 DartMessenger#send 方法。

在 android 中的 send 方法中,callback 就被转换成了 resposeId,callback 被保存在 pendingReplies 中,而后调用 flutterJNI.dispatchPlatformMessage,经过 jni 调用 DispatchPlatformMessage 函数,后面就是通过 jni 函数再调用 PlatformViewAndroid::DispatchPlatformMessage,在这个函数中,它也是对 callback 进行了进一步的封装成了 PlatformMessageResponseAndroid,可是这里的 callback 是在 android 中转换以后的 responseId。同时把 name 和 PlatformMessageResponseAndroid 一块儿封装成 PlatformMessage,这个与前面从 flutter 中发送消息的过程是相似的。

完了以后再通过 PlatformView、Shell 传递到达 engine 中,

void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
  if (message->channel() == kLifecycleChannel) {
    if (HandleLifecyclePlatformMessage(message.get()))
      return;
  } else if (message->channel() == kLocalizationChannel) {
    if (HandleLocalizationPlatformMessage(message.get()))
      return;
  } else if (message->channel() == kSettingsChannel) {
    HandleSettingsPlatformMessage(message.get());
    return;
  }

  if (runtime_controller_->IsRootIsolateRunning() &&
      runtime_controller_->DispatchPlatformMessage(std::move(message))) {
    return;
  }

  // If there's no runtime_, we may still need to set the initial route.
  if (message->channel() == kNavigationChannel) {
    HandleNavigationPlatformMessage(std::move(message));
    return;
  }

  FML_DLOG(WARNING) << "Dropping platform message on channel: "
                    << message->channel();
}
复制代码

在这里 engine 会尝试拦截部分的消息,好比使用 HandleLifecyclePlatformMessage 先处理一下生命周期相关的消息,根据它的返回值决定是否会拦截,不过从函数的目前实现来看,是不会拦截的,这里的更多的意义应该在于这些消息在 engine 和 flutter 中都有被使用到,好比生命周期事件,会影响到 engine 中 activity_running_ 和 flutter 中 _framesEnabled 的值,从功用上看,两者实际上是一致的,只不过位于不一样层,而且两者之间貌似目前只能经过这里来统一状态。

后面还会拦截 localization 和 setting 消息,而后才是调用 DispatchPlatformMessage 继续向 flutter 层传递消息,若是当前还处于初始化阶段,runtime_ 未初始化完成时,会直接由 engine 处理 navigation 事件(目前只接收 setInitialRoute),将 initialRoute 保存起来,这个值会在 flutter 启动时做为初始的 route 来建立对应的 Widget 。

正常的传递,还要通过 RuntimeController 到 window,在 window 中再将 PlatformMessageResponseAndroid 存到 pending_responses_ 中,提供 responseId,再拆解 PlatformMessage,经过 DartInvokeField 调用 dart 中的 _dispatchPlatformMessage 进入到 flutter 里。

void _dispatchPlatformMessage(String name, ByteData data, int responseId) {
  if (window.onPlatformMessage != null) {
    _invoke3<String, ByteData, PlatformMessageResponseCallback>(
      window.onPlatformMessage,
      window._onPlatformMessageZone,
      name,
      data,
      (ByteData responseData) {
        window._respondToPlatformMessage(responseId, responseData);
      },
    );
  } else {
    window._respondToPlatformMessage(responseId, null);
  }
}
复制代码

如上,这里声明了一个 callback 函数,做为入参调用 window.onPlatformMessage,也就是在 ServicesBinding 中给出的 defaultBinaryMessenger.handlePlatformMessage。

Future<void> handlePlatformMessage(
  String channel,
  ByteData data,
  ui.PlatformMessageResponseCallback callback,
) async {
  ByteData response;
  try {
    final MessageHandler handler = _handlers[channel];
    if (handler != null)
      response = await handler(data);
  } catch (exception, stack) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'services library',
      context: ErrorDescription('during a platform message callback'),
    ));
  } finally {
    callback(response);
  }
}
复制代码

接收到消息以后,BinaryMessager 须要选择合适的 MessageHandler 处理消息,MessageHandler 则是经过 setMessageHandler 进行注册的,有三种不一样的 MessageHandler,分别对应着 BasicMessageChannel、MethodChannel 和 EventChannel ,就是是下面分类传输的内容,总之,通过 MessageHandler 处理以后会获得 ByteData 类型的处理结果,最终由 callback 将结果回传给 native 。

callback 中调用的就是 window._respondToPlatformMessage,对应 engine 中的函数 RespondToPlatformMessage。这里会根据 response 是否为空决定调用 CompletePlatformMessageEmptyResponse 仍是 CompletePlatformMessageResponse。

在 CompletePlatformMessageResponse 中,会根据 responseId 再找回 PlatformMessageResponseAndroid,调用其 Complete 将数据传递给 android。

void PlatformMessageResponseAndroid::Complete(
    std::unique_ptr<fml::Mapping> data) {
  platform_task_runner_->PostTask(
      fml::MakeCopyable([response = response_id_,               //
                         weak_java_object = weak_java_object_,  //
                         data = std::move(data)                 //
  ]() {
        // We are on the platform thread. Attempt to get the strong reference to
        // the Java object.
        auto* env = fml::jni::AttachCurrentThread();
        auto java_object = weak_java_object.get(env);

        if (java_object.is_null()) {
          // The Java object was collected before this message response got to
          // it. Drop the response on the floor.
          return;
        }

        // Convert the vector to a Java byte array.
        fml::jni::ScopedJavaLocalRef<jbyteArray> data_array(
            env, env->NewByteArray(data->GetSize()));
        env->SetByteArrayRegion(
            data_array.obj(), 0, data->GetSize(),
            reinterpret_cast<const jbyte*>(data->GetMapping()));

        // Make the response call into Java.
        FlutterViewHandlePlatformMessageResponse(env, java_object.obj(),
                                                 response, data_array.obj());
      }));
}
复制代码

这里的 FlutterViewHandlePlatformMessageResponse 就是 jni 的函数,由 jni 将数据传递给 FlutterJNI 的 handlePlatformMessageResponse。

在 handlePlatformMessageResponse 的实现中,会根据 replyId 从 pendingReplies 中找到 callback,也就是 send 方法中传进的,callback 是由上层实现的。

以上就是基础传输过程当中,两个方向的处理步骤,在这个流程中主要关注的是消息的传递,基本不关心消息的类型及处理(不过在 Engine、Shell 类中仍是有对消息的拦截处理)。

分类传输

分类传输主要关心的就是数据的类型,在 flutter(包括 android 中也存在与之对应的)中存在三种类型的消息,就是 BasicMessageChannel、MethodChanel 和 EventChannel,每一 Channel 的构造都至少须要两个参数,与之绑定的消息类型(消息的 name)和编码方式(MessageCodec/MethodCodec)。

BasicMessageChannel

BasicMessageChannel 用于双向的单次消息传递,包括发送消息、接收消息两个功能。发送消息使用的为 send 函数,返回值是 Future,

Future<T> send(T message) async {
  return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
}
复制代码

如上,BasicMessageChannel 主要的封装就是在调用 binaryMessenger.send 先后对 message 进行编解码,从这里也能够看出,send 函数的参数与返回值是同一种类型的(虽然可使用 dynamic 抹除 T 的做用)。

而 setMessageHandler 则是用于向 binaryMessenger 注册一个消息处理函数,

void setMessageHandler(Future<T> handler(T message)) {
  if (handler == null) {
    binaryMessenger.setMessageHandler(name, null);
  } else {
    binaryMessenger.setMessageHandler(name, (ByteData message) async {
      return codec.encodeMessage(await handler(codec.decodeMessage(message)));
    });
  }
}
复制代码

handler 是具体的处理逻辑,BasicMessageChannel 所作的也仍是在处理先后将 message 编解码。

数据转换流程

从以上能够看出,一次 BasicMessageChannel 的 send 过程当中,message 的类型转换有以下过程:

T(in flutter) -> ByteData(in flutter) -> DartByteData(in engine) -> buffer(in engine) -> vector<uint8_t>(in engine) -> byte[](in jni) -> ByteBuffer(in java) -> T(in java) -> process -> ByteBuffer(in java) -> vector<uint8_t>(in engine) -> DataMapping(in engine) -> ByteData(in engine) -> T(in flutter)

MethodChannel

MethodCannel 用于双向的方法调用,包括调用对端方法、响应对端调用,流程与 BasicMessageChannel 基本相似,只是数据转换上略有不一样,invokeMethod 用于调用对端方法,setMethodCallHandler 用于注册一个方法的处理函数。

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;
}
复制代码

invokeMethod 有两个参数,方法名和方法入参,在 MethodCannel 中这两个参数会被封装成 MethodCall 对象,而后利用 MethodCodec 将 MethodCall 编码成 ByteData 传递到 binaryMessenger,并获得 ByteData 类型的返回值,后面再利用 MethodCodec 将其解码成 T 类型数据,由此能够看到,MethodCannel 的 invokeMethod 函数的入参与返回值是不一样类型的,MethodCodec 相对于 MessageCode 多了两个函数,分别用于编解码 MethodCall 和编解码方法调用的返回值。

而 setMethodCallHandler 用于注册调用方法供 native 调用,

void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
  binaryMessenger.setMessageHandler(
    name,
    handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
  );
}

Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
  final MethodCall call = codec.decodeMethodCall(message);
  try {
    return codec.encodeSuccessEnvelope(await handler(call));
  } on PlatformException catch (e) {
    return codec.encodeErrorEnvelope(
      code: e.code,
      message: e.message,
      details: e.details,
    );
  } on MissingPluginException {
    return null;
  } catch (e) {
    return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
  }
}
复制代码

它会将 handler 封装到 callback 函数中,在 _handleAsMethodCall 中进行协调,也就是先把 message 转成 MethodCall 对象以后,再交给 handler 处理,处理完的结果再由 codec 转成 ByteData 并返回,接着 binaryMessenger 再拿着这个数据回传到 native 中。

数据转换流程

flutter 调用 native 函数:

(method, message) -> MethodCall(in flutter) -> ByteData(in flutter) -> ...(一系列基础传输流程) -> ByteBuffer(in java) -> MethodCall(in java) -> process -> Object(in java) -> ByteBuffer(in java) -> ...(一系列基础传输流程) -> ByteData(in flutter) -> T

navtive 调用 flutter 函数:

(method, message) -> MethodCall(in java) -> ByteBuffer(in java) -> ...(一系列基础传输流程) -> ByteData(in flutter) -> MethodCall(in flutter) -> process -> dynamic(in flutter) -> ByteData(in flutter) -> ...(一系列基础传输流程) -> byte[](in java) -> ByteBuffer(in java) -> Object(in java)

另外,MethodChannel 还有一个子类,OptionalMethodChannel,支持返回 null 结果,这在 MethodChannel 中是不容许的。

EventChannel

EventChannel 用于接收一系列消息,这些消息被包装到 Stream 中,receiveBroadcastStream 返回一个 Stream 对象,能够在 Stream 上添加监听者,而后开始发送 listen 消息到 native 层后,native 中对应的 EventChannel 就开始处理消息,在处理的过程当中能够发送多条消息,这些消息的在传到 flutter 中后都会被加入到 Stream 中,而后通知全部的监听者。

在 listen 开始以前,native 中的 EventChannel 没法主动开始工做,当 flutter 中 Stream 的第一个监听者成功添加时,才会向 native 发送 listen 消息,此时,两者之间创建链接,开始工做。

在 listen 的过程当中间,两者并不能进行交流,native 层收到 listen 消息就开始不断发送消息,flutter 中也只能经过 Stream 不断接收消息。

两者均可以主动结束链接,在 native 中能够调用 EventSink.endOfStream 结束链接,在 flutter 中移除 Stream 的全部监听者也能够结束链接。若是须要再次创建链接,就须要向 Stream 中添加监听者。

由上述能够得出,在 EventChannel 中,native 与 flutter 之间不平等,且两者向对方发送消息时都不须要接收返回的结果,而是直接经过新的 send 途径向对方发送消息,这是 EventChannel 与以上两种方式最大的不一样。

下面具体介绍 EventChannel 的工做原理,EventChannel 在初始化时须要提供 name 和 MethodCodec,而后调用 receiveBroadcastStream 返回一个 Stream 对象,后续上层主要是经过在 Stream 中添加监听来完成对 native 层消息的接收,同时添加/删除监听的操做,能够开始/结束接收消息。

Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
  final MethodChannel methodChannel = MethodChannel(name, codec);
  StreamController<dynamic> controller;
  controller = StreamController<dynamic>.broadcast(onListen: () async {
    defaultBinaryMessenger.setMessageHandler(name, (ByteData reply) async {
      if (reply == null) {
        controller.close();
      } else {
        try {
          controller.add(codec.decodeEnvelope(reply));
        } on PlatformException catch (e) {
          controller.addError(e);
        }
      }
      return null;
    });
    try {
      await methodChannel.invokeMethod<void>('listen', arguments);
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context: ErrorDescription('while activating platform stream on channel $name'),
      ));
    }
  }, onCancel: () async {
    defaultBinaryMessenger.setMessageHandler(name, null);
    try {
      await methodChannel.invokeMethod<void>('cancel', arguments);
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context: ErrorDescription('while de-activating platform stream on channel $name'),
      ));
    }
  });
  return controller.stream;
}
复制代码

EventChannel 的主要逻辑都在上面这个函数中了,调用以后会先生成一个 MethodChannel 用于向 native 发送消息,而后就是建立了一个 StreamController,在它的 onListen 函数中注册了一个消息接收,在接收到消息以后会将其转存到 StreamController 中,StreamController 负责将消息发送到它的监听者们,注册以后就是向 native 发送一个 listen 消息,开始接收。在 onCancel 函数中取消消息接收,同时发送一个 cancel 消息给 native ,让 native 中的 EventChannel 作一些结尾工做等。

再看看 native (以 android 为例)中 EventChannel 的工做原理,EventChannel 经过调用 setStreamHandler 方法,StreamHandler 由上层提供,负责具体的处理逻辑,它有 onListen 和 onCancel 两个方法,前者提供了 arguments(参数)和 events(用于发送消息、报错和结束链接),后者只提供了 argments 参数。

在 setStreamHandler 中,EventChannel 将 StreamHandler 封装到 IncomingStreamRequestHandler 中,IncomingStreamRequestHandler 中的 onMessage 就是 native 接收到消息以后的回调方法,接收到 onListen 消息会调用 onListen 函数,在 onListen 中,除去一些维护正常状态的代码,就是调用 StreamHandler 的 listen 函数。native 中的 EventChannel 也能够经过调用 EventSink.endOfStream 结束链接,它会向 flutter 中发送一个空消息,在 flutter 中接收到空消息时便会调用 StreamController.close 结束接收消息。

上层调用

对于上层调用,flutter 中主要集中在 SystemChannels 中,好比 navigation、lifecycle 等。

navigation

navigation 是一个 MethodChannel,在 WidgetsBinding 中会给它设置监听,调用 SystemChannels.navigation.setMethodCallHandler 传递进去 _handleNavigationInvocation 函数,

Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
  switch (methodCall.method) {
    case 'popRoute':
      return handlePopRoute();
    case 'pushRoute':
      return handlePushRoute(methodCall.arguments);
  }
  return Future<dynamic>.value();
}
复制代码

如上,这里会根据 MethodCall 的方法名调用不一样调用 pop/push 函数完成页面切换。

lifecycle

lifcycle 是一个 BasicMessageChannel,在 SchedulerBinding 中设置监听,传进的 handler 为 _handleLifecycleMessage,

Future<String> _handleLifecycleMessage(String message) async {
  handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message));
  return null;
}

void handleAppLifecycleStateChanged(AppLifecycleState state) {
  assert(state != null);
  _lifecycleState = state;
  switch (state) {
    case AppLifecycleState.resumed:
    case AppLifecycleState.inactive:
      _setFramesEnabledState(true);
      break;
    case AppLifecycleState.paused:
    case AppLifecycleState.suspending:
      _setFramesEnabledState(false);
      break;
  }
}
复制代码

如上,会根据 native 传来的 activity 的状态,决定是否容许更新界面。

总结

以上就是 flutter 与 native 之间进行消息传递的过程、实现原理以及一些使用方法,经过消息传递机制,flutter 可以十分方便的调用 native 的能力,这一点的意义主要在于如下两点:

  1. 可以在 flutter 中使用一些 native 特有的能力,好比 shared_preference 这个用于本地持久化的 flutter 插件,它的实现就依赖于 native,好比 android 中的 SharedPreference,ios 中的 UserDefaults,这种状况下,在三端分别实现 MethodChannel,flutter 中就能够在 android/ios 平台中使用统一的调用方法。
  2. 方便在 flutter 中复用 native 中已有的能力,好比在已有的项目中加入部分 flutter 页面,那么在这些 flutter 页面中也能够很方便地使用一些项目中原有的能力,下降开发成本。