Flutter与Native交互源码分析(一)

作者: 小波_po | 来源:发表于2019-03-26 17:27 被阅读13次

    Dart层的消息传递分析

    static const MethodChannel _channel = const MethodChannel('kwl_native');
    
    final int result = await _channel.invokeMethod('getBatteryLevel');
    

    在与原生交互时,首先创建MethodChannel对象,接着调用invokeMethod()方法并传入原生方法名。接着查看invokeMethod()源码。

      @optionalTypeArgs
      Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
        assert(method != null);
        final ByteData result = await BinaryMessages.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;
      }
    

    从里层往外分析,首先MethodCall(method, arguments)将方法名和参数进行封装成MethodCall对象便于传递。codecMethodCodec对象,而MethodCodec是抽象类,所以是其实现类的对象,在MethodChannel创建时候默认创建StandardMethodCodec对象。

    class MethodChannel {
      /// Creates a [MethodChannel] with the specified [name].
      ///
      /// The [codec] used will be [StandardMethodCodec], unless otherwise
      /// specified.
      ///
      /// Neither [name] nor [codec] may be null.
      const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
    
      /// The logical channel on which communication happens, not null.
      final String name;
    
      /// The message codec used by this channel, not null.
      final MethodCodec codec;
    

    接着看StandardMethodCodec源码

    /// 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');
      }
    
    ......
    

    列了主要的编码和解码的方法,将MethodCall对象通过StandardMessageCodec进行读写,转为ByteData与C++层进行传输。查看writeValue()方法可以知道传输支持的类型。

     void writeValue(WriteBuffer buffer, dynamic value) {
        if (value == null) {
          buffer.putUint8(_valueNull);
        } else if (value is bool) {
          buffer.putUint8(value ? _valueTrue : _valueFalse);
        } else if (value is int) {
          if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
            buffer.putUint8(_valueInt32);
            buffer.putInt32(value);
          } else {
            buffer.putUint8(_valueInt64);
            buffer.putInt64(value);
          }
        } else if (value is double) {
          buffer.putUint8(_valueFloat64);
          buffer.putFloat64(value);
        } else if (value is String) {
          buffer.putUint8(_valueString);
          final List<int> bytes = utf8.encoder.convert(value);
          writeSize(buffer, bytes.length);
          buffer.putUint8List(bytes);
        } else if (value is Uint8List) {
          buffer.putUint8(_valueUint8List);
          writeSize(buffer, value.length);
          buffer.putUint8List(value);
        } else if (value is Int32List) {
          buffer.putUint8(_valueInt32List);
          writeSize(buffer, value.length);
          buffer.putInt32List(value);
        } else if (value is Int64List) {
          buffer.putUint8(_valueInt64List);
          writeSize(buffer, value.length);
          buffer.putInt64List(value);
        } else if (value is Float64List) {
          buffer.putUint8(_valueFloat64List);
          writeSize(buffer, value.length);
          buffer.putFloat64List(value);
        } else if (value is List) {
          buffer.putUint8(_valueList);
          writeSize(buffer, value.length);
          for (final dynamic item in value) {
            writeValue(buffer, item);
          }
        } else if (value is Map) {
          buffer.putUint8(_valueMap);
          writeSize(buffer, value.length);
          value.forEach((dynamic key, dynamic value) {
            writeValue(buffer, key);
            writeValue(buffer, value);
          });
        } else {
          throw ArgumentError.value(value);
        }
      }
    

    再回到invokeMethod()中,接着看BinaryMessages,它用于处理二进制消息,用于发送消息到插件或从插件接受消息。

     final ByteData result = await BinaryMessages.send(
          name,
          codec.encodeMethodCall(MethodCall(method, arguments)),
        );
    

    查看send()方法,发送到指定渠道名的插件中,渠道名需保证唯一。

      /// Send a binary message to the platform plugins on the given channel.
      ///
      /// Returns a [Future] which completes to the received response, undecoded, in
      /// binary form.
      static Future<ByteData> send(String channel, ByteData message) {
        final _MessageHandler handler = _mockHandlers[channel];
        if (handler != null)
          return handler(message);
        return _sendPlatformMessage(channel, message);
      }
    

    这里_mockHandlers[channel]模拟处理器拦截并相应消息,一般为空,所以会走到_sendPlatformMessage ()方法。

    static 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: 'during a platform message response callback',
            ));
          }
        });
        return completer.future;
      }
    

    最终调用ui.window. sendPlatformMessage (),跟进查看

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

    可以看到最后调用一个native方法,类似java中定义的native方法一样,将渠道名、数据和处理回调传去C++层。
    至此,Dart层的消息传递分析完毕。

    相关文章

      网友评论

        本文标题:Flutter与Native交互源码分析(一)

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