美文网首页
EventChannel、MethodChannel原理

EventChannel、MethodChannel原理

作者: 就叫汉堡吧 | 来源:发表于2022-03-09 17:15 被阅读0次
    • 概述

      在《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<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
        final MethodChannel methodChannel = MethodChannel(name, codec);
        late StreamController<dynamic> controller;
        controller = StreamController<dynamic>.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<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 {
          binaryMessenger.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;
      }
      

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

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

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

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

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

      StreamSubscription<T> _subscribe(void onData(T data)?, Function? onError,
          void onDone()?, bool cancelOnError) {
        if (isClosed) {
          return new _DoneStreamSubscription<T>(onDone);
        }
        var subscription = new _BroadcastSubscription<T>(
            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<T> subscription) {
        assert(identical(subscription._next, subscription));
        subscription._eventState = (_state & _STATE_EVENT_ID);
        // Insert in linked list as last subscription.
        _BroadcastSubscription<T>? 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<dynamic, Object, StackTrace>(handleError);
      }
      if (handleError is void Function(Object)) {
       return zone.registerUnaryCallback<dynamic, Object>(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<T>() {
                  @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原理

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