Flutter圈子flutter学习笔记Flutter中文社区

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

2019-03-26  本文已影响13人  小波_po

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层的消息传递分析完毕。

上一篇下一篇

猜你喜欢

热点阅读