EventChannel、MethodChannel原理

  • 概述

    在《Flutter原生通信原理概述》一文中我们大概知道了Flutter是怎样和原生通信的,当时我们提到了EventChannel和MethodChannel,实际上还有一个Channel就是BasicMessageChannel。

    它们彼此相互独立,并没有继承自什么共有的父类,但是它们的原理是差不多的。

  • BinaryMessenger

    上述三个Channel都持有一个BinaryMessenger类型的messager对象,就是它负责原生向Flutter发送数据的。

    BinaryMessenger本身是一个接口,实现它的类有DartExecutor、DartMessenger、DefaultBinaryMessenger,它里面有一个send方法,最终就是通过它来发送数据。

    发送数据必然要通过FlutterEngine,在FlutterPlugin的onAttachedToEngine方法中有一个FlutterPlugin.FlutterPluginBinding类型的binding参数:

    public FlutterPluginBinding(
        @NonNull Context applicationContext,
        @NonNull FlutterEngine flutterEngine,
        @NonNull BinaryMessenger binaryMessenger,
        @NonNull TextureRegistry textureRegistry,
        @NonNull PlatformViewRegistry platformViewRegistry,
        @NonNull FlutterAssets flutterAssets)
    

    往Channel传递的就是它的binaryMessenger。

    FlutterPlugin.FlutterPluginBinding在FlutterEngineConnectionRegistry的构造方法里初始化的:

    FlutterEngineConnectionRegistry(
        @NonNull Context appContext,
        @NonNull FlutterEngine flutterEngine,
        @NonNull FlutterLoader flutterLoader) {
      this.flutterEngine = flutterEngine;
      pluginBinding =
          new FlutterPlugin.FlutterPluginBinding(
              appContext,
              flutterEngine,
              flutterEngine.getDartExecutor(),
              flutterEngine.getRenderer(),
              flutterEngine.getPlatformViewsController().getRegistry(),
              new DefaultFlutterAssets(flutterLoader));
    }
    

    可见,它的binaryMessenger就是flutterEngine的DartExecutor。

    看一下DartExecutor的send方法:

    public void send(@NonNull String channel, @Nullable ByteBuffer message) {
      binaryMessenger.send(channel, message);
    }
    

    binaryMessenger是DefaultBinaryMessenger:

    this.dartMessenger = new DartMessenger(flutterJNI);
    dartMessenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler);
    this.binaryMessenger = new DefaultBinaryMessenger(dartMessenger);
    

    它的send方法是:

    public void send(@NonNull String channel, @Nullable ByteBuffer message) {
      messenger.send(channel, message, null);
    }
    

    可见其实就是调用了DartMessenger的send方法:

    public void send(@NonNull String channel, @NonNull ByteBuffer message) {
      Log.v(TAG, "Sending message over channel '" + channel + "'");
      send(channel, message, null);
    }
    
    @Override
    public void send(
        @NonNull String channel,
        @Nullable ByteBuffer message,
        @Nullable BinaryMessenger.BinaryReply callback) {
      Trace.beginSection("DartMessenger#send on " + channel);
      Log.v(TAG, "Sending message with callback over channel '" + channel + "'");
    
      try {
        int replyId = nextReplyId++;
        if (callback != null) {
          pendingReplies.put(replyId, callback);
        }
        if (message == null) {
          flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
        } else {
          flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
        }
      } finally {
        Trace.endSection();
      }
    }
    

    可见最后调用了FlutterJNI来发送数据。

    在DartMessenger中还定义了一个方法:handleMessageFromDart,这个方法是在收到任何Channel的消息时就会被FlutterJNI调用。

    @Override
    public void handleMessageFromDart(
        @NonNull final String channel,
        @Nullable ByteBuffer message,
        final int replyId,
        long messageData) {
      // Called from the ui thread.
      Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
      @Nullable final HandlerInfo handlerInfo = messageHandlers.get(channel);
      @Nullable
      final DartMessengerTaskQueue taskQueue = (handlerInfo != null) ? handlerInfo.taskQueue : null;
      Runnable myRunnable =
          () -> {
            Trace.beginSection("DartMessenger#handleMessageFromDart on " + channel);
            try {
              invokeHandler(handlerInfo, message, replyId);
              ... ...
            } finally {
              // This is deleting the data underneath the message object.
              flutterJNI.cleanupMessageData(messageData);
              Trace.endSection();
            }
          };
      @NonNull
      final DartMessengerTaskQueue nonnullTaskQueue =
          taskQueue == null ? platformTaskQueue : taskQueue;
      nonnullTaskQueue.dispatch(myRunnable);
    }
    

    核心代码在invokeHandler中:

    private void invokeHandler(
        @Nullable HandlerInfo handlerInfo, @Nullable ByteBuffer message, final int replyId) {
      // Called from any thread.
      if (handlerInfo != null) {
        try {
          Log.v(TAG, "Deferring to registered handler to process message.");
          handlerInfo.handler.onMessage(message, new Reply(flutterJNI, replyId));
        } catch (Exception ex) {
          Log.e(TAG, "Uncaught exception in binary message listener", ex);
          flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
        } catch (Error err) {
          handleError(err);
        }
      } else {
        Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
        flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
      }
    }
    

    到这一步为止,所有的Channel都会走上面的逻辑,从这里的handlerInfo.handler.onMessage开始有所不一样了,因为不同的Channel的handler不同。

  • EventChannel

    EventChannel通过setStreamHandler方法设置handler:

    @UiThread
    public void setStreamHandler(final StreamHandler handler) {
      if (taskQueue != null) {
        messenger.setMessageHandler(
            name, handler == null ? null : new IncomingStreamRequestHandler(handler), taskQueue);
      } else {
        messenger.setMessageHandler(
            name, handler == null ? null : new IncomingStreamRequestHandler(handler));
      }
    }
    

    可见,这里设置的handler其实是IncomingStreamRequestHandler,它的onMessage方法如下:

    @Override
    public void onMessage(ByteBuffer message, final BinaryReply reply) {
      final MethodCall call = codec.decodeMethodCall(message);
      if (call.method.equals("listen")) {
        onListen(call.arguments, reply);
      } else if (call.method.equals("cancel")) {
        onCancel(call.arguments, reply);
      } else {
        reply.reply(null);
      }
    }
    

    为什么这里会判断method的名字是“listen”和“cancel”呢?我们来看dart端发送的逻辑。

    之前博文我们知道,dart监听的代码为:eventChannel.receiveBroadcastStream().listen(...),receiveBroadcastStream方法为:

    Stream receiveBroadcastStream([ dynamic arguments ]) {
      final MethodChannel methodChannel = MethodChannel(name, codec);
      late StreamController controller;
      controller = StreamController.broadcast(onListen: () async {
        binaryMessenger.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('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 {
        binaryMessenger.setMessageHandler(name, null);
        try {
          await methodChannel.invokeMethod('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;
    }
    

    broadcast方法会返回 _AsyncBroadcastStreamController ,它的stream在其父类 _BroadcastStreamController中实现,得到的是 _BroadcastStream,现在我们知道 _AsyncBroadcastStreamController中持有了onListen和onCancel回调,再来看 _BroadcastStream的listen方法,这个方法最终在 _StreamImpl中找到:

    StreamSubscription listen(void onData(T data)?,
        {Function? onError, void onDone()?, bool? cancelOnError}) {
      cancelOnError ??= false;
      StreamSubscription subscription =
          _createSubscription(onData, onError, onDone, cancelOnError);
      _onListen(subscription);
      return subscription;
    }
    

    _createSubscription方法在 _StreamImpl的上一级子类 _ControllerStream中重写:

    StreamSubscription _createSubscription(void onData(T data)?,
            Function? onError, void onDone()?, bool cancelOnError) =>
        _controller._subscribe(onData, onError, onDone, cancelOnError);
    

    我们知道 _BroadcastStream的 _controller就是前面的 _AsyncBroadcastStreamController, _subscribe方法在 _BroadcastStreamController中实现:

    StreamSubscription _subscribe(void onData(T data)?, Function? onError,
        void onDone()?, bool cancelOnError) {
      if (isClosed) {
        return new _DoneStreamSubscription(onDone);
      }
      var subscription = new _BroadcastSubscription(
          this, onData, onError, onDone, cancelOnError);
      _addListener(subscription);
      if (identical(_firstSubscription, _lastSubscription)) {
        // Only one listener, so it must be the first listener.
        _runGuarded(onListen);
      }
      return subscription;
    }
    

    这里会把创建的_BroadcastSubscription通过 _addListener方法添加到一个链表结构中:

    void _addListener(_BroadcastSubscription subscription) {
      assert(identical(subscription._next, subscription));
      subscription._eventState = (_state & _STATE_EVENT_ID);
      // Insert in linked list as last subscription.
      _BroadcastSubscription? oldLast = _lastSubscription;
      _lastSubscription = subscription;
      subscription._next = null;
      subscription._previous = oldLast;
      if (oldLast == null) {
        _firstSubscription = subscription;
      } else {
        oldLast._next = subscription;
      }
    }
    

    然后执行_runGuarded方法,还记得onListen是什么吗,就是前面broadcast方法里传入的onListen回调函数, _runGuarded方法就是执行它:

    void _runGuarded(void Function()? notificationHandler) {
      if (notificationHandler == null) return;
      try {
        notificationHandler();
      } catch (e, s) {
        Zone.current.handleUncaughtError(e, s);
      }
    }
    

    我们回过头来看onListen里面做了什么。

    回看上面的receiveBroadcastStream方法的代码,我们发现,onListen中其实就是使用一个MethodChannel来invokeMethod的,方法名正是“listen”,调用invokeMethod方法之后会调用到原生绑定的同名EventChannel设置的StreamHandler的onListen方法中。

    所以现在就可以理解IncomingStreamRequestHandler的onMessage方法的逻辑了,回到onMessage方法,接着会调用IncomingStreamRequestHandler的onListen方法:

    private void onListen(Object arguments, BinaryReply callback) {
      final EventSink eventSink = new EventSinkImplementation();
      final EventSink oldSink = activeSink.getAndSet(eventSink);
      if (oldSink != null) {
        // Repeated calls to onListen may happen during hot restart.
        // We separate them with a call to onCancel.
        try {
          handler.onCancel(null);
        } catch (RuntimeException e) {
          Log.e(TAG + name, "Failed to close existing event stream", e);
        }
      }
      try {
        handler.onListen(arguments, eventSink);
        callback.reply(codec.encodeSuccessEnvelope(null));
      } catch (RuntimeException e) {
        activeSink.set(null);
        Log.e(TAG + name, "Failed to open event stream", e);
        callback.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
      }
    }
    

    这里的callback.reply会回调到系统的Platform的Channel中,是系统级通知,和我们的业务不相关。可以看到,传到StreamHandler的eventSink是EventSinkImplementation,它对EventSink的实现如下:

    @Override
    @UiThread
    public void success(Object event) {
      if (hasEnded.get() || activeSink.get() != this) {
        return;
      }
      EventChannel.this.messenger.send(name, codec.encodeSuccessEnvelope(event));
    }
    
    @Override
    @UiThread
    public void error(String errorCode, String errorMessage, Object errorDetails) {
      if (hasEnded.get() || activeSink.get() != this) {
        return;
      }
      EventChannel.this.messenger.send(
          name, codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
    }
    
    @Override
    @UiThread
    public void endOfStream() {
      if (hasEnded.getAndSet(true) || activeSink.get() != this) {
        return;
      }
      EventChannel.this.messenger.send(name, null);
    }
    

    可见,EventChannel发送给Flutter端的方式也是通过内部的BinaryMessenger来完成的,只不过它的messenger是私有的且没提供任何方法可以直接操作它,这就使得EventChannel向Flutter端的发送基于Flutter的发送请求,也就是说,只有Flutter通过同名的EventChannel向原生发送数据之后,原生EventChannel才能在接收回调中通过EventSinkImplementation这个入口调用BinaryMessenger来完成向Flutter端的发送,这也是EventChannel区别于MethodChannel的关键所在。

    这里调用EventChannel的messenger的send方法后,会回调到flutter端,前面我们知道broadcast方法设置的onListen回调函数中,binaryMessenger设置了MessageHandler回调函数。

    回看代码可知,先看reply(即EventSink的messenger发送的数据)如果不为空的情况,会调用controller的add方法,参数是原生返回的数据,add逻辑绕了很大的一圈之后(感兴趣的可以自己看,太多了就不贴了)会走到_BufferingStreamSubscription的 _sendData 方法中,里面有一句:

    _zone.runUnaryGuarded(_onData, data);
    

    _onData就是Stream的listen方法传入的onData函数,最终runUnaryGuarded会执行onData函数,通常我们会在onData函数中更新UI或者其他的操作,比如:

    eventChannel.receiveBroadcastStream().listen(
      (event) {
        onReceiveBatteryChange(event);
      },
      onError: onReceiveBatteryWrong,
      onDone: onReceiveBatteryDone
    );
    

    如果在add流程中出错了则会调用controller的addError方法走出错逻辑,这个时候会回调到上面的onError回调函数中,原理同add一样。当你不再需要时,原生端可以调用eventSink.endOfStream方法,这时发送的message就是null,从而在flutter端的MessageHandler中会走reply为null的逻辑,即会调用controller.close方法,最终会调用onDone回调。

    这样完成了一次完整的EventChannel的通信过程。

    小记.

    上面的onError是一个Function类型,其并没有指定Function的参数和返回值,但是静态编译时会有警告,运行就会出错,经查,在应用过程中会有类型检查从而导致运行报错:

    static Function _registerErrorHandler(Zone zone, Function? handleError) {
    // TODO(lrn): Consider whether we need to register the null handler.
    handleError ??= _nullErrorHandler;
    if (handleError is void Function(Object, StackTrace)) {
     return zone
         .registerBinaryCallback(handleError);
    }
    if (handleError is void Function(Object)) {
     return zone.registerUnaryCallback(handleError);
    }
    throw new ArgumentError("handleError callback must take either an Object "
       "(the error), or both an Object (the error) and a StackTrace.");
    }
    

    所以,Flutter不只是有方法定义处的Function限制,在运行流程中凡是用到的地方不符合都会报错,而编译时只是会提示警告而已。

  • MethodChannel

    MethodChannel的大体流程和EventChannel是大致相同的,只不过有几点不太一样。

    1. MethodChannel有公开方法invokeMethod,所以它可以主动随时发送数据,不像EventChannel需要Flutter端的listen发起。

    2. EventChannel虽然也是调用的MethodChannel的invokeMethod方法回调到原生的onListen中,但是它的方法名固定是“listen”,而且不能传自定义参数,所以它本身是为了和原生建立起连接,告诉原生把有价值的数据传给哪个EventChannel,然后Flutter端会有回调来通过这个约定的EventChannel接收;而MethodChannel则可以用来调用两端的任何方法,传递任何参数。

    3. MethodChannel通过setMethodCallHandler方法设置MethodCallHandler,BinaryMessenger的setMessageHandler方法传入的BinaryMessageHandler是IncomingMethodCallHandler,它的onMessage方法的逻辑是:

      @Override
      @UiThread
      public void onMessage(ByteBuffer message, final BinaryReply reply) {
        final MethodCall call = codec.decodeMethodCall(message);
        try {
          handler.onMethodCall(
              call,
              new Result() {
                @Override
                public void success(Object result) {
                  reply.reply(codec.encodeSuccessEnvelope(result));
                }
      
                @Override
                public void error(String errorCode, String errorMessage, Object errorDetails) {
                  reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
                }
      
                @Override
                public void notImplemented() {
                  reply.reply(null);
                }
              });
        } catch (RuntimeException e) {
          Log.e(TAG + name, "Failed to handle method call", e);
          reply.reply(
              codec.encodeErrorEnvelopeWithStacktrace(
                  "error", e.getMessage(), null, getStackTrace(e)));
        }
      }
      

      最终在onMethodCall中调用result的success、error或notImplemented方法来返回结果数据,以Fluter端发送给原生端为例,Flutter端会接收到一个Future实例,通过它可以取得返回数据:

      Future result = await methodChannel.invokeMethod("getName");
      
  • BasicMessageChannel

    同样,BasicMessageChannel也是一样的原理,不同的是:

    1. 和MethodChannel相比,它的公开发送数据的api方法是send。

    2. 它的BinaryMessageHandler设置的是IncomingMessageHandler,IncomingMessageHandler的onMessage方法是:

      @Override
      public void onMessage(@Nullable ByteBuffer message, @NonNull final BinaryReply callback) {
        try {
          handler.onMessage(
              codec.decodeMessage(message),
              new Reply() {
                @Override
                public void reply(T reply) {
                  callback.reply(codec.encodeMessage(reply));
                }
              });
        } catch (RuntimeException e) {
          Log.e(TAG + name, "Failed to handle message", e);
          callback.reply(null);
        }
      }
      

      可见,它的回调方法是MessageHandler的onMessage。

    可以看到,其实BasicMessageChannel和MethodChannel很相似,只不过它这里接收到的“数据”就是数据,而MethodChannel接收到的“数据”是方法名,我们要根据方法名去调不同的方法。

  • 总结

    可见,这三种Channel的原理殊途同归,其实是一样的,内部都是通过DartMessenger来调用FlutterJNI的相关API完成通信的,只不过Flutter对他们做了不同的封装以适用不同的场景。

    • EventChannel的作用是为了提供一个专门的通道,原生端通过这个专门的通道发送数据给Flutter端,从而Flutter端监听的位置可以及时响应这个变化,它需要dart端调用listen方法建立和原生的连接,只能是从原生端往Flutter端发送。
    • MethodChannel是为了两端的方法调用,两端都可以发送和传输,传递的数据是方法的名字,在onMethodCall回调中根据方法名是什么来执行不同的操作。
    • BasicMessageChannel是为了普通的数据传输,回调中获取的就是实际的数据,它代表的就是数据本身,同样可以两端互传和接收。

你可能感兴趣的:(EventChannel、MethodChannel原理)