Flutter原理篇:硬核-从Platform到Dart通信原理分析之Android篇

哈喽大家好,新年又见面了,不知道大家有没有想我呢,我可是一有空就想着为大家更新博客呢😁,上一期给大家分享了 <Flutter运行原理篇之paint重绘原理> ,本来说这期应该是接上一期分享 <Flutter运行原理篇之Compose合成原理的>,但是今天咋们插入一篇《从Platform到Dart通信原理分析之Android篇》来说一说从Native也就是PlatForm端到Dart的通信原理,这个也是我一直都想写的内容了,好了让我们愉快的开始吧

其实经常逛论坛的都知道已经有一些文章分析过了这个的运行原理了为什么我还要写呢,

  • 一个我觉得他们水平没有问题但是博客里面有一些小的漏洞,小瑕疵作为有洁癖,不将就的工程师,大家肯定不是很满意

  • 第二也是最关键的一点就是那些文章基本上是一气呵成的要不就是作者没有设计到更深入的层次去分析,而是点到而止就“打道回府”了,要么就是水平很高但是内容不够详细丰富,特别是不够通俗易懂的去给讲解,导致大家看了他们写的文章以后有一种“失落”的感觉,觉得大致都能看懂但就是理解不了什么意思,于是就有了我们今天这篇文章,看我能不能带着大家 通·俗·易·懂· 的去理解一下这个原理的过程,我保证绝对会让大家看完以后长舒一口气感叹:“原来原理就是这么的‘简单’啊”,希望大家在末尾给我评论留言,every body let’s go

说到了运行原理,那么我们就带大家来复习一下从Platform到Dart的使用会基本用到的几个类,来作为文章的预热:首先我们要知道的是一般来说Native到Flutter端相互通信有三种调用的方式,也就是三种不同的Channel,两端通过同一个Channel来进行互相的通信,分别是一下三个Channel:

  • BasicMessageChannel:用于传递字符串和半结构化的信息。
  • MethodChannel:用于传递方法调用(method invocation)。
  • EventChannel: 用于数据流(event streams)的通信。

三种Channel之间互相独立,各有用途,但它们在设计上却非常相近,所以我们分析的时候可能就只会分析几种情况即可,不会所有的情况都分析到

好了说了这么多,咋们再来看看这三个通道的基本使用:MethodChanel,BasicMessageChannel,EventChannel这三个通道在Dart端以及Platform端都有对应相同名字的类来作为双方通信/接收的使用,我们以BasicMessageChannel来举例子:Dart端以及Android端首先都需要实例化这个Channel并且名字也要一样,并且都需要提供一个send发送方法,以及一个接收的方法来作为监听,例如:

BasicMessageChannel Dart端代码片段:

//初始化
static const BasicMessageChannel<String> _basicMessageChannel =
    const BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());

//设置监听函数
_basicMessageChannel
    .setMessageHandler((String message) => Future<String>(() {
          setState(() {
            showMessage = 'BasicMessageChannel:'+message;
          });
          return "收到Native的消息ab:" + message;
        }));


//向Native端发送消息
response = await _basicMessageChannel.send(value);

BasicMessageChannel Android端代码片段:

//初始化
this.messageChannel = new BasicMessageChannel<>(messenger, "BasicMessageChannelPlugin", StringCodec.INSTANCE);

//设置监听函数
@Override
public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//处理Dart发来的消息
    reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
    if (activity instanceof IShowMessage) {
        ((IShowMessage) activity).onShowMessage(s);
    }
    Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
}

//向Dart端发送消息
messageChannel.send(message, callback);

好了,使用就是这么简单,我们现从双方的构造方法开始说起,不论是Dart端还是Native端的构造函数都需要这三个元素:

  • name: String类型,代表Channel的名字,也是其唯一标识符。
  • messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。
  • codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。
  1. Channel name
    一个Flutter应用中可能存在多个不同的Channel,每个Channel在创建时必须指定一个独一无二的name,Channel之间使用name来区分彼此,当有消息从Flutter端发送到Native端时会根据其传递过来的channel name找到该Channel对应的Handler(消息处理器)

  2. 消息信使:BinaryMessenger

Pasted Graphic.png

虽然三种Channel各有用途,但是他们与Flutter通信的工具却是相同的,均为BinaryMessager,在Dart端默认是_DefaultBinaryMessenger,在Android端默认是DartExecutor

  1. MessageCodec
    MessageCodec用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel所使用的编解码器默认就是MessageCodec

我们重点看看后面两个,先来看看BinaryMessager的使用:

BinaryMessager:

在Dart端默认是_DefaultBinaryMessenger

class _DefaultBinaryMessenger extends BinaryMessenger {
}

在Android端默认是DartExecutor

public class DartExecutor implements BinaryMessenger {
  private static final String TAG = "DartExecutor";
}

BinaryMessenger是Platform端与Flutter端通信的中间人,其通信使用的消息格式为二进制格式数据(理解这个很重要,这个也是数据传递的核心),当我们初始化一个Channel,并向该Channel注册处理消息的Handler时,实际上会生成一个与之对应的BinaryMessageHandler,并以channel name为key,注册到BinaryMessenger中:

Android的注册步骤:

public class EventChannelPlugin implements EventChannel.StreamHandler {
    private EventChannel.EventSink eventSink;

    static EventChannelPlugin registerWith(BinaryMessenger messenger) {
        EventChannelPlugin plugin = new EventChannelPlugin();
        new EventChannel(messenger, "EventChannelPlugin").setStreamHandler(plugin);
        return plugin;
    }
}

Android的messenger 默认是DartExecutor

public void setStreamHandler(final StreamHandler handler) {
  messenger.setMessageHandler(
      name, handler == null ? null : new IncomingStreamRequestHandler(handler));
}

最终是存在DartMessenger里面的messageHandlers Map里面

class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
  private static final String TAG = "DartMessenger";

  @NonNull private final FlutterJNI flutterJNI;
  @NonNull private final Map<String, BinaryMessenger.BinaryMessageHandler> messageHandlers;
  @NonNull private final Map<Integer, BinaryMessenger.BinaryReply> pendingReplies;
  private int nextReplyId = 1;

  DartMessenger(@NonNull FlutterJNI flutterJNI) {
    this.flutterJNI = flutterJNI;
    this.messageHandlers = new HashMap<>();
    this.pendingReplies = new HashMap<>();
  }

  @Override
  public void setMessageHandler(
      @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
    if (handler == null) {
      Log.v(TAG, "Removing handler for channel '" + channel + "'");
      messageHandlers.remove(channel);
    } else {
      Log.v(TAG, "Setting handler for channel '" + channel + "'");
      messageHandlers.put(channel, handler);
    }
  }
}

差不多就是这两个方法了,很简单不再赘述,好了再说回来我们再看看Dart方面的BinaryMessenger与BinaryMessageHandler是怎么样的呢,上面说了BinaryMessenger默认是_DefaultBinaryMessenger,我们来看看他的注册setMessageHandler方法:

BasicMessageChannel.setMessageHandler:

/// Sets a callback for receiving messages from the platform plugins on this
/// channel. Messages may be null.
///
/// The given callback will replace the currently registered callback for this
/// channel, if any. To remove the handler, pass null as the `handler`
/// argument.
///
/// The handler's return value is sent back to the platform plugins as a
/// message reply. It may be null.
void setMessageHandler(Future<T> Function(T? message)? handler) {
  if (handler == null) {
    binaryMessenger.setMessageHandler(name, null);
  } else {
    binaryMessenger.setMessageHandler(name, (ByteData? message) async {
      return codec.encodeMessage(await handler(codec.decodeMessage(message)));
    });
  }
}

_DefaultBinaryMessenger.setMessageHandler:

@override
void setMessageHandler(String channel, MessageHandler? handler) {
  if (handler == null) {
    _handlers.remove(channel);
  } else {
    _handlers[channel] = handler;
    ui.channelBuffers.drain(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
      await handlePlatformMessage(channel, data, callback);
    });
  }
}

把MessageHandler存在了 static final Map<String, MessageHandler> _handlers = <String, MessageHandler>{}; 之中

上面说的是注册,我们看看监听是什么样子的:

当Dart端发送消息到Android端的BinaryMessenger时,BinaryMessenger会根据其入参channel找到对应的BinaryMessageHandler,并交由其处理,我们看看Android端是什么样的

DartMessenger.handleMessageFromDart

@Override
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);
    } catch (Error err) {
      handleError(err);
    }
  } else {
    Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
    flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
  }
}

他会通过messageHandlers取出来对应的BinaryMessageHandler,调用其注册的onMessage方法(如果忘记了使用的话,可以往上查看Android端的使用步骤)

现在再去看看当Android端发送消息到Dart端的BinaryMessenger时他的处理是怎么样的呢:

@override
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);
    } else {
      ui.channelBuffers.push(channel, data, callback!);
      callback = null;
    }
  } catch (exception, stack) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'services library',
      context: ErrorDescription('during a platform message callback'),
    ));
  } finally {
    if (callback != null) {
      callback(response);
    }
  }
}

在Dart端通过也是根据channel的名字从_handlers取出MessageHandler直接调用await handler(data); 方法,等待监听方法返回数据然后再通过callback传回给Android端

好了上面我们看了Android与Dart两端简单注册以及收发消息的步骤,现在我们再来看看上面提到的另一个元素编解码器Codec:


MessageCodec:

MessageCodec用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel所使用的编解码器默认就是MessageCodec,MessageCodec有多种不同的实现,分为以下几种:

  • BinaryCodec

BinaryCodec是最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已,或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝

Android部分

public final class BinaryCodec implements MessageCodec<ByteBuffer> {
  // This codec must match the Dart codec of the same name in package flutter/services.
  public static final BinaryCodec INSTANCE = new BinaryCodec();

  private BinaryCodec() {}

  @Override
  public ByteBuffer encodeMessage(ByteBuffer message) {
    return message;
  }

  @Override
  public ByteBuffer decodeMessage(ByteBuffer message) {
    return message;
  }
}

Dart部分

class BinaryCodec implements MessageCodec<ByteData> {
  /// Creates a [MessageCodec] with unencoded binary messages represented using
  /// [ByteData].
  const BinaryCodec();

  @override
  ByteData? decodeMessage(ByteData? message) => message;

  @override
  ByteData? encodeMessage(ByteData? message) => message;
}

都是直接返回数据,代码太简单不解释了

  • StringCodec
    StringCodec用于字符串与二进制数据之间的编解码,其编码格式默认为UTF-8 :

Dart部分

class StringCodec implements MessageCodec<String> {
  /// Creates a [MessageCodec] with UTF-8 encoded String messages.
  const StringCodec();

  @override
  String? decodeMessage(ByteData? message) {
    if (message == null)
      return null;
    return utf8.decoder.convert(message.buffer.asUint8List(message.offsetInBytes, message.lengthInBytes));
  }

  @override
  ByteData? encodeMessage(String? message) {
    if (message == null)
      return null;
    final Uint8List encoded = utf8.encoder.convert(message);
    return encoded.buffer.asByteData();
  }
}

Android部分

public final class StringCodec implements MessageCodec<String> {
  private static final Charset UTF8 = Charset.forName("UTF8");
  public static final StringCodec INSTANCE = new StringCodec();

  private StringCodec() {}

  @Override
  public ByteBuffer encodeMessage(String message) {
    if (message == null) {
      return null;
    }
    // TODO(mravn): Avoid the extra copy below.
    final byte[] bytes = message.getBytes(UTF8);
    final ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
    buffer.put(bytes);
    return buffer;
  }

  @Override
  public String decodeMessage(ByteBuffer message) {
    if (message == null) {
      return null;
    }
    final byte[] bytes;
    final int offset;
    final int length = message.remaining();
    if (message.hasArray()) {
      bytes = message.array();
      offset = message.arrayOffset();
    } else {
      // TODO(mravn): Avoid the extra copy below.
      bytes = new byte[length];
      message.get(bytes);
      offset = 0;
    }
    return new String(bytes, offset, length, UTF8);
  }
}

StringCodec默认都是UTF8进行编解码,代码也很简单不详细叙述

  • JSONMessageCodec
    JSONMessageCodec用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典,在Android端则使用了其自定义的JSONTokener与StringCodec作为序列化工具。

Android部分 JsonCodec.encode / JsonCodec.decode

class JSONMessageCodec implements MessageCodec<Object?> {
  // The codec serializes messages as defined by the JSON codec of the
  // dart:convert package. The format used must match the Android and
  // iOS counterparts.

  /// Creates a [MessageCodec] with UTF-8 encoded JSON messages.
  const JSONMessageCodec();

  @override
  ByteData? encodeMessage(Object? message) {
    if (message == null)
      return null;
    return const StringCodec().encodeMessage(json.encode(message));
  }

  @override
  dynamic decodeMessage(ByteData? message) {
    if (message == null)
      return message;
    return json.decode(const StringCodec().decodeMessage(message)!);
  }
}

Dart部分 JsonCodec.encode / JsonCodec.decode

String encode(Object? value, {Object? toEncodable(dynamic object)?}) {
  toEncodable ??= _toEncodable;
  if (toEncodable == null) return encoder.convert(value);
  return JsonEncoder(toEncodable).convert(value);
}


dynamic decode(String source,
    {Object? reviver(Object? key, Object? value)?}) {
  reviver ??= _reviver;
  if (reviver == null) return decoder.convert(source);
  return JsonDecoder(reviver).convert(source);
}

Dart与Android部分原理是一样的,编码首先通过JsonCodec.encode进行编码,然后再通过StringCodec 编码成ByteData;解码部分首先通过StringCodec解码成字符串再通过JsonCodec.decode解码Json字符串

  • StandardMessageCodec
    StandardMessageCodec是BasicMessageChannel的默认编解码器,常见类型基本上都支持,使用方式稍微复杂一点我们重点介绍下

Dart部分StandardMessageCodec.encodeMessage / StandardMessageCodec.decodeMessage

@override
ByteData? encodeMessage(Object? message) {
  if (message == null)
    return null;
  final WriteBuffer buffer = WriteBuffer();
  writeValue(buffer, message);
  return buffer.done();
}


@override
dynamic decodeMessage(ByteData? message) {
  if (message == null)
    return null;
  final ReadBuffer buffer = ReadBuffer(message);
  final Object? result = readValue(buffer);
  if (buffer.hasRemaining)
    throw const FormatException('Message corrupted');
  return result;
}

Dart端通过WriteBuffer,ReadBuffer作为字符的基本写入与读取的数据结构的封装,然后通过writeValue,readValue写入以及读取数据

void writeValue(WriteBuffer buffer, Object? value) {
  if (value == null) {
    buffer.putUint8(_valueNull);
  } else if (value is bool) {
    buffer.putUint8(value ? _valueTrue : _valueFalse);
  } else if (value is double) {  // Double precedes int because in JS everything is a double.
                                 // Therefore in JS, both `is int` and `is double` always
                                 // return `true`. If we check int first, we'll end up treating
                                 // all numbers as ints and attempt the int32/int64 conversion,
                                 // which is wrong. This precedence rule is irrelevant when
                                 // decoding because we use tags to detect the type of value.
    buffer.putUint8(_valueFloat64);
    buffer.putFloat64(value);
  } 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 String) {
    buffer.putUint8(_valueString);
    final Uint8List 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 Object? item in value) {
      writeValue(buffer, item);
    }
  } else if (value is Map) {
    buffer.putUint8(_valueMap);
    writeSize(buffer, value.length);
    value.forEach((Object? key, Object? value) {
      writeValue(buffer, key);
      writeValue(buffer, value);
    });
  } else {
    throw ArgumentError.value(value);
  }
}

writeValue首先先写入1个字节作为数据的类型,然后再写入数据本身的值;如果是“复杂”数据类型比如String,Uint8List,Int32List等等写入数据类型的同时还会写入数据的长度再写入数据的值;如果数据再“复杂”点比如List,Map最后会对他们的值(Map会对Key,value)分别再调用writeValue去写入;

Object? readValue(ReadBuffer buffer) {
  if (!buffer.hasRemaining)
    throw const FormatException('Message corrupted');
  final int type = buffer.getUint8();
  return readValueOfType(type, buffer);
}

/// Reads a value of the indicated [type] from [buffer].
///
/// The codec can be extended by overriding this method, calling super for
/// types that the extension does not handle. See the discussion at
/// [writeValue].
Object? readValueOfType(int type, ReadBuffer buffer) {
  switch (type) {
    case _valueNull:
      return null;
    case _valueTrue:
      return true;
    case _valueFalse:
      return false;
    case _valueInt32:
      return buffer.getInt32();
    case _valueInt64:
      return buffer.getInt64();
    case _valueFloat64:
      return buffer.getFloat64();
    case _valueLargeInt:
    case _valueString:
      final int length = readSize(buffer);
      return utf8.decoder.convert(buffer.getUint8List(length));
    case _valueUint8List:
      final int length = readSize(buffer);
      return buffer.getUint8List(length);
    case _valueInt32List:
      final int length = readSize(buffer);
      return buffer.getInt32List(length);
    case _valueInt64List:
      final int length = readSize(buffer);
      return buffer.getInt64List(length);
    case _valueFloat64List:
      final int length = readSize(buffer);
      return buffer.getFloat64List(length);
    case _valueList:
      final int length = readSize(buffer);
      final List<Object?> result = List<Object?>.filled(length, null, growable: false);
      for (int i = 0; i < length; i++)
        result[i] = readValue(buffer);
      return result;
    case _valueMap:
      final int length = readSize(buffer);
      final Map<Object?, Object?> result = <Object?, Object?>{};
      for (int i = 0; i < length; i++)
        result[readValue(buffer)] = readValue(buffer);
      return result;
    default: throw const FormatException('Message corrupted');
  }
}

readValue读取数据的时候就是一个逆向操作,先读取1个字节的数据类型,再读取数据的值,读取值的时候如果遇到“复杂”数据类型先读取他的长度在读取他的值,总之就是writeValue的逆操作,说完了Dart部分,让我们再来看看Android部分:

Android部分 StandardMessageCodec.encodeMessage / StandardMessageCodec.decodeMessage

@Override
public ByteBuffer encodeMessage(Object message) {
  if (message == null) {
    return null;
  }
  final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
  writeValue(stream, message);
  final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
  buffer.put(stream.buffer(), 0, stream.size());
  return buffer;
}


@Override
public Object decodeMessage(ByteBuffer message) {
  if (message == null) {
    return null;
  }
  message.order(ByteOrder.nativeOrder());
  final Object value = readValue(message);
  if (message.hasRemaining()) {
    throw new IllegalArgumentException("Message corrupted");
  }
  return value;
}

Android端通过ExposedByteArrayOutputStream作为字符的基本写入与读取的数据结构的封装,然后通过writeValue,readValue写入以及读取数据,ExposedByteArrayOutputStream 其实就是ByteArrayOutputStream的子类

protected void writeValue(ByteArrayOutputStream stream, Object value) {
  if (value == null || value.equals(null)) {
    stream.write(NULL);
  } else if (value instanceof Boolean) {
    stream.write(((Boolean) value).booleanValue() ? TRUE : FALSE);
  } else if (value instanceof Number) {
    if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
      stream.write(INT);
      writeInt(stream, ((Number) value).intValue());
    } else if (value instanceof Long) {
      stream.write(LONG);
      writeLong(stream, (long) value);
    } else if (value instanceof Float || value instanceof Double) {
      stream.write(DOUBLE);
      writeAlignment(stream, 8);
      writeDouble(stream, ((Number) value).doubleValue());
    } else if (value instanceof BigInteger) {
      stream.write(BIGINT);
      writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8));
    } else {
      throw new IllegalArgumentException("Unsupported Number type: " + value.getClass());
    }
  } else if (value instanceof String) {
    stream.write(STRING);
    writeBytes(stream, ((String) value).getBytes(UTF8));
  } else if (value instanceof byte[]) {
    stream.write(BYTE_ARRAY);
    writeBytes(stream, (byte[]) value);
  } else if (value instanceof int[]) {
    stream.write(INT_ARRAY);
    final int[] array = (int[]) value;
    writeSize(stream, array.length);
    writeAlignment(stream, 4);
    for (final int n : array) {
      writeInt(stream, n);
    }
  } else if (value instanceof long[]) {
    stream.write(LONG_ARRAY);
    final long[] array = (long[]) value;
    writeSize(stream, array.length);
    writeAlignment(stream, 8);
    for (final long n : array) {
      writeLong(stream, n);
    }
  } else if (value instanceof double[]) {
    stream.write(DOUBLE_ARRAY);
    final double[] array = (double[]) value;
    writeSize(stream, array.length);
    writeAlignment(stream, 8);
    for (final double d : array) {
      writeDouble(stream, d);
    }
  } else if (value instanceof List) {
    stream.write(LIST);
    final List<?> list = (List) value;
    writeSize(stream, list.size());
    for (final Object o : list) {
      writeValue(stream, o);
    }
  } else if (value instanceof Map) {
    stream.write(MAP);
    final Map<?, ?> map = (Map) value;
    writeSize(stream, map.size());
    for (final Entry<?, ?> entry : map.entrySet()) {
      writeValue(stream, entry.getKey());
      writeValue(stream, entry.getValue());
    }
  } else {
    throw new IllegalArgumentException("Unsupported value: " + value);
  }
}

writeValue首先先写入1个字节作为数据的类型,然后再写入数据本身的值;如果是“复杂”数据类型比如String,写入数据类型的同时还会写入数据的长度再写入数据的值;如果是数组的话还会先写入对齐字符,他的长度大小是数据元素的单个长度大小;如果数据再“复杂”点比如List,Map最后会对他们的值(Map会对Key,value)分别再调用writeValue去写入;

Object? readValue(ReadBuffer buffer) {
  if (!buffer.hasRemaining)
    throw const FormatException('Message corrupted');
  final int type = buffer.getUint8();
  return readValueOfType(type, buffer);
}

/// Reads a value of the indicated [type] from [buffer].
///
/// The codec can be extended by overriding this method, calling super for
/// types that the extension does not handle. See the discussion at
/// [writeValue].
Object? readValueOfType(int type, ReadBuffer buffer) {
  switch (type) {
    case _valueNull:
      return null;
    case _valueTrue:
      return true;
    case _valueFalse:
      return false;
    case _valueInt32:
      return buffer.getInt32();
    case _valueInt64:
      return buffer.getInt64();
    case _valueFloat64:
      return buffer.getFloat64();
    case _valueLargeInt:
    case _valueString:
      final int length = readSize(buffer);
      return utf8.decoder.convert(buffer.getUint8List(length));
    case _valueUint8List:
      final int length = readSize(buffer);
      return buffer.getUint8List(length);
    case _valueInt32List:
      final int length = readSize(buffer);
      return buffer.getInt32List(length);
    case _valueInt64List:
      final int length = readSize(buffer);
      return buffer.getInt64List(length);
    case _valueFloat64List:
      final int length = readSize(buffer);
      return buffer.getFloat64List(length);
    case _valueList:
      final int length = readSize(buffer);
      final List<Object?> result = List<Object?>.filled(length, null, growable: false);
      for (int i = 0; i < length; i++)
        result[i] = readValue(buffer);
      return result;
    case _valueMap:
      final int length = readSize(buffer);
      final Map<Object?, Object?> result = <Object?, Object?>{};
      for (int i = 0; i < length; i++)
        result[readValue(buffer)] = readValue(buffer);
      return result;
    default: throw const FormatException('Message corrupted');
  }
}

/** Reads a value as written by writeValue. */
protected final Object readValue(ByteBuffer buffer) {
  if (!buffer.hasRemaining()) {
    throw new IllegalArgumentException("Message corrupted");
  }
  final byte type = buffer.get();
  return readValueOfType(type, buffer);
}

/**
 * Reads a value of the specified type.
 *
 * <p>Subclasses may extend the codec by overriding this method, calling super for types that the
 * extension does not handle.
 */
protected Object readValueOfType(byte type, ByteBuffer buffer) {
  final Object result;
  switch (type) {
    case NULL:
      result = null;
      break;
    case TRUE:
      result = true;
      break;
    case FALSE:
      result = false;
      break;
    case INT:
      result = buffer.getInt();
      break;
    case LONG:
      result = buffer.getLong();
      break;
    case BIGINT:
      {
        final byte[] hex = readBytes(buffer);
        result = new BigInteger(new String(hex, UTF8), 16);
        break;
      }
    case DOUBLE:
      readAlignment(buffer, 8);
      result = buffer.getDouble();
      break;
    case STRING:
      {
        final byte[] bytes = readBytes(buffer);
        result = new String(bytes, UTF8);
        break;
      }
    case BYTE_ARRAY:
      {
        result = readBytes(buffer);
        break;
      }
    case INT_ARRAY:
      {
        final int length = readSize(buffer);
        final int[] array = new int[length];
        readAlignment(buffer, 4);
        buffer.asIntBuffer().get(array);
        result = array;
        buffer.position(buffer.position() + 4 * length);
        break;
      }
    case LONG_ARRAY:
      {
        final int length = readSize(buffer);
        final long[] array = new long[length];
        readAlignment(buffer, 8);
        buffer.asLongBuffer().get(array);
        result = array;
        buffer.position(buffer.position() + 8 * length);
        break;
      }
    case DOUBLE_ARRAY:
      {
        final int length = readSize(buffer);
        final double[] array = new double[length];
        readAlignment(buffer, 8);
        buffer.asDoubleBuffer().get(array);
        result = array;
        buffer.position(buffer.position() + 8 * length);
        break;
      }
    case LIST:
      {
        final int size = readSize(buffer);
        final List<Object> list = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
          list.add(readValue(buffer));
        }
        result = list;
        break;
      }
    case MAP:
      {
        final int size = readSize(buffer);
        final Map<Object, Object> map = new HashMap<>();
        for (int i = 0; i < size; i++) {
          map.put(readValue(buffer), readValue(buffer));
        }
        result = map;
        break;
      }
    default:
      throw new IllegalArgumentException("Message corrupted");
  }
  return result;
}

/** Reads alignment padding bytes as written by writeAlignment. */
protected static final void readAlignment(ByteBuffer buffer, int alignment) {
  final int mod = buffer.position() % alignment;
  if (mod != 0) {
    buffer.position(buffer.position() + alignment - mod);
  }
}

readValue读取数据的时候就是一个逆向操作,先读取1个字节的数据类型,再读取数据的值,读取值的时候如果遇到“复杂”数据类型先读取他的长度在读取他的值,值得一提的是如果是数组类型的话会先读出他的对齐字符,目的是为了校验当前的position位置是否可以持续读取数组元素类型,

  • MethodCodec

MethodCodec用于二进制数据与方法调用(MethodCall)和返回结果之间的编解码,MethodChannel和EventChannel所使用的编解码器均为MethodCodec,由于MethodCodec编解码基本上原理是依赖于MessageCodec的,所以我们大致讲解下即可:

  1. StandardMethodCodec

Android部分 StandardMethodCodec.encodeMethodCall / StandardMethodCodec.decodeMethodCall

//send发送消息,调用编码过程
@UiThread
public void invokeMethod(String method, @Nullable Object arguments, @Nullable Result callback) {
  messenger.send(
      name,
      codec.encodeMethodCall(new MethodCall(method, arguments)),
      callback == null ? null : new IncomingResultHandler(callback));
}


@Override
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
  final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
  messageCodec.writeValue(stream, methodCall.method);
  messageCodec.writeValue(stream, methodCall.arguments);
  final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
  buffer.put(stream.buffer(), 0, stream.size());
  return buffer;
}



@Override
public MethodCall decodeMethodCall(ByteBuffer methodCall) {
  methodCall.order(ByteOrder.nativeOrder());
  final Object method = messageCodec.readValue(methodCall);
  final Object arguments = messageCodec.readValue(methodCall);
  if (method instanceof String && !methodCall.hasRemaining()) {
    return new MethodCall((String) method, arguments);
  }
  throw new IllegalArgumentException("Method call corrupted");
}

函数调用的时候首先把method方法名,argument参数封装成一个MethodCall,然后分别对于函数名以及参数调用messageCodec.writeValue方法写入;解码的时候分别读出method方法名,argument参数然后包装成一个MethodCall

Dart部分 StandardMethodCodec.encodeMethodCall / StandardMethodCodec.decodeMethodCall

@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 Object? method = messageCodec.readValue(buffer);
  final Object? arguments = messageCodec.readValue(buffer);
  if (method is String && !buffer.hasRemaining)
    return MethodCall(method, arguments);
  else
    throw const FormatException('Invalid method call');
}

Dart部分原理一模一样,不在赘述

  1. JSONMethodCodec
    原理与上面较为相似,不再赘述

最后再附上一张表来说明上面代码的Type类型与Dart与PlatForm类型对应的表:

Pasted Graphic 1.png

好了,我们现在从BinaryMessenger到BinaryMessengerHandler再到消息编解码器Codec基本都讲清楚了,下面就剩下最关键的怎么去通信了,我们只以BasicMessageChannel来说明情况(因为MethodChannel,EventChannel原理差不多),我们先从Dart端向Android端发送消息看看他是怎么进行的:


Dart端向Android端发送消息:

//发送代码片段
response = await _basicMessageChannel.send(value);

BasicMessageChannel.send


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

发送的时候先把消息经过Codec编码处理(Codec我们上面讲解过了)再调用发送,send函数返回的是一个Future,是因为发送端需要await等待返回值,binaryMessenger在Dart端默认是_DefaultBinaryMessenger上面也讲过了

_DefaultBinaryMessenger.send

@override
Future<ByteData?>? send(String channel, ByteData? message) {
  final MessageHandler? handler = _mockHandlers[channel];
  if (handler != null)
    return handler(message);
  return _sendPlatformMessage(channel, message);
}

_DefaultBinaryMessenger._sendPlatformMessage

Future<ByteData?> _sendPlatformMessage(String channel, ByteData? message) {
  final Completer<ByteData?> completer = Completer<ByteData?>();
  // ui.PlatformDispatcher.instance is accessed directly instead of using
  // ServicesBinding.instance.platformDispatcher 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.PlatformDispatcher.instance because the PlatformDispatcher may be
  // dependency injected elsewhere with a different instance. However, static
  // access at this location seems to be the least bad option.
  ui.PlatformDispatcher.instance.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;
}

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

String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data)
    native 'PlatformConfiguration_sendPlatformMessage';

_sendPlatformMessage是一个C++方法,现在我们会从Dart层进入到C++层了,我们先借用网上的一个图来看看

Pasted Graphic 2.png

这个图说明了整个Dart层与C++层的函数调用栈,我们不会每一个函数调用都走一遍这没有太多的意义,但是我也不会什么都不说了只上一个图来敷衍大家,我会把关键步骤带大家看一看(😁这个也是我们今天这个博客与其他的博客不同的地方),首先我们来看看这个C++函数再哪注册的

void PlatformConfiguration::RegisterNatives(
    tonic::DartLibraryNatives* natives) {
  natives->Register({
      {"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true},
      {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
      {"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4,
       true},
      {"PlatformConfiguration_respondToPlatformMessage",
       _RespondToPlatformMessage, 3, true},
      {"PlatformConfiguration_render", Render, 3, true},
      {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true},
      {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2,
       true},
      {"PlatformConfiguration_reportUnhandledException",
       ReportUnhandledException, 2, true},
      {"PlatformConfiguration_setNeedsReportTimings", SetNeedsReportTimings, 2,
       true},
      {"PlatformConfiguration_getPersistentIsolateData",
       GetPersistentIsolateData, 1, true},
      {"PlatformConfiguration_computePlatformResolvedLocale",
       _ComputePlatformResolvedLocale, 2, true},
  });
}

注册到了_SendPlatformMessage函数里面,进去看看

void _SendPlatformMessage(Dart_NativeArguments args) {
  tonic::DartCallStatic(&SendPlatformMessage, args);
}


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->platform_configuration()) {
    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->platform_configuration()->client()->HandlePlatformMessage(
        std::make_unique<PlatformMessage>(name, response));
  } else {
    tonic::DartByteData data(data_handle);
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
    dart_state->platform_configuration()->client()->HandlePlatformMessage(
        std::make_unique<PlatformMessage>(
            name, fml::MallocMapping::Copy(buffer, data.length_in_bytes()),
            response));
  }

  return Dart_Null();
}

大家看明白了没有,Dart端的_sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) 函数对应的是C++ 的 SendPlatformMessage(Dart_Handle window,const std::string& name,Dart_Handle callback,Dart_Handle data_handle) 这个函数,大家注意观察下Dart的参数都已经转换为了C++层的Dart_Handle 这个指针来处理,这边还把callback封装成了一个PlatformMessageResponseDart对象来处理,并且值得一提的是这里还把channel的名字,data数据以及封装过了的callback再封装成了一个PlatformMessage对象作为下面函数栈调用的传递的参数

// |PlatformView|
void PlatformMessageHandlerAndroid::HandlePlatformMessage(
    std::unique_ptr<flutter::PlatformMessage> message) {
  // Called from the ui thread.
  int response_id = next_response_id_++;
  if (auto response = message->response()) {
    std::lock_guard lock(pending_responses_mutex_);
    pending_responses_[response_id] = response;
  }
  // This call can re-enter in InvokePlatformMessageXxxResponseCallback.
  jni_facade_->FlutterViewHandlePlatformMessage(std::move(message),
                                                response_id);
}

我们再看到倒数第三步HandlePlatformMessage这个函数,这里生产了一个唯一的response_id与message->response()也就是我们上面提到的PlatformMessageResponse对象(其实是对callback的封装)绑定起来,然后把PlatformMessageResponse对象(其实是对callback的封装)存在键值为response_id的pending_responses_里面,再把response_id发送给Android端,如果需要回调的话,Android会把返回值以及这个response_id返回,pending_responses_查找到对应的Dart端的response再执行

void PlatformViewAndroidJNIImpl::FlutterViewHandlePlatformMessage(
    std::unique_ptr<flutter::PlatformMessage> message,
    int responseId) {
  // Called from the ui thread.
  JNIEnv* env = fml::jni::AttachCurrentThread();

  auto java_object = java_object_.get(env);
  if (java_object.is_null()) {
    return;
  }

  fml::jni::ScopedJavaLocalRef<jstring> java_channel =
      fml::jni::StringToJavaString(env, message->channel());

  if (message->hasData()) {
    fml::jni::ScopedJavaLocalRef<jobject> message_array(
        env, env->NewDirectByteBuffer(
                 const_cast<uint8_t*>(message->data().GetMapping()),
                 message->data().GetSize()));
    // Message data is deleted in CleanupMessageData.
    fml::MallocMapping mapping = message->releaseData();
    env->CallVoidMethod(java_object.obj(), g_handle_platform_message_method,
                        java_channel.obj(), message_array.obj(), responseId,
                        mapping.Release());
  } else {
    env->CallVoidMethod(java_object.obj(), g_handle_platform_message_method,
                        java_channel.obj(), nullptr, responseId, nullptr);
  }

  FML_CHECK(fml::jni::CheckException(env));
}

g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
    env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
if (g_flutter_jni_class->is_null()) {
  FML_LOG(ERROR) << "Failed to find FlutterJNI Class.";
  return false;
}

g_handle_platform_message_method =
    env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
                     "(Ljava/lang/String;Ljava/nio/ByteBuffer;IJ)V");

最后面回到这个函数jni_facade_->FlutterViewHandlePlatformMessage部分,通过JNI去调用Android端handlePlatformMessage函数,我们再来看看Android这个函数是什么样子的:

FlutterJNI.handlePlatformMessage

// Called by native.
// TODO(mattcarroll): determine if message is nonull or nullable
@SuppressWarnings("unused")
@VisibleForTesting
public void handlePlatformMessage(
    @NonNull final String channel, byte[] message, final int replyId) {
  if (platformMessageHandler != null) {
    platformMessageHandler.handleMessageFromDart(channel, message, replyId);
  }
  // TODO(mattcarroll): log dropped messages when in debug mode
  // (https://github.com/flutter/flutter/issues/25391)
}

这里的platformMessageHandler其实就是初始化赋值的DartMessenger

DartMessenger.handleMessageFromDart

@Override
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);
    } catch (Error err) {
      handleError(err);
    }
  } else {
    Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
    flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
  }
}

这里面通过messageHandlers.get(channel)取出了handler(至于怎么放置的我们在上面注册的时候已经讲过了),然后调用其监听的handler.onMessage方法,这个在上面设置setMessageHandler的时候已经设置好了监听了,现在我们再来看看Dart调用Android端以后Dart端的回调函数是怎么运行的

Dart端向Android端发送消息以后Dart端的回调处理:

@Override
public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//处理Dart发来的消息
    reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
    if (activity instanceof IShowMessage) {
        ((IShowMessage) activity).onShowMessage(s);
    }
    Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
}

Android端回调通过reply.reply进行,reply则为上面参数的Reply对象

static class Reply implements BinaryMessenger.BinaryReply {
  @NonNull private final FlutterJNI flutterJNI;
  private final int replyId;
  private final AtomicBoolean done = new AtomicBoolean(false);

  Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
    this.flutterJNI = flutterJNI;
    this.replyId = replyId;
  }

  @Override
  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());
    }
  }
}

调用到了invokePlatformMessageResponseCallback这个函数进行回调处理,并且把replyId,以及reply返回值传过去,返回值也是ByteBuffer类型的

public void invokePlatformMessageResponseCallback(
    int responseId, @Nullable ByteBuffer message, int position) {
  ensureRunningOnMainThread();
  if (isAttached()) {
    nativeInvokePlatformMessageResponseCallback(
        nativeShellHolderId, responseId, message, position);
  } else {
    Log.w(
        TAG,
        "Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: "
            + responseId);
  }
}

他对应的是一个C++函数nativeInvokePlatformMessageResponseCallback,现在我们又准备进入到C++层了

{
    .name = "nativeInvokePlatformMessageResponseCallback",
    .signature = "(JILjava/nio/ByteBuffer;I)V",
    .fnPtr =
        reinterpret_cast<void*>(&InvokePlatformMessageResponseCallback),
},

他对应的是一个InvokePlatformMessageResponseCallback函数,最后面回到这个PlatformMessageHandlerAndroid里面的InvokePlatformMessageResponseCallback函数

void PlatformMessageHandlerAndroid::InvokePlatformMessageResponseCallback(
    int response_id,
    std::unique_ptr<fml::Mapping> mapping) {
  // Called from any thread.
  if (!response_id) {
    return;
  }
  // TODO(gaaclarke): Move the jump to the ui thread here from
  // PlatformMessageResponseDart so we won't need to use a mutex anymore.
  fml::RefPtr<flutter::PlatformMessageResponse> message_response;
  {
    std::lock_guard lock(pending_responses_mutex_);
    auto it = pending_responses_.find(response_id);
    if (it == pending_responses_.end())
      return;
    message_response = std::move(it->second);
    pending_responses_.erase(it);
  }

  message_response->Complete(std::move(mapping));
}

message_response就是PlatformMessageResponse也就是我们一开始把回调callback封装成的PlatformMessageResponseDart对象看看他的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 =
            tonic::DartByteData::Create(data->GetMapping(), data->GetSize());
        tonic::DartInvoke(callback.Release(), {byte_buffer});
      }));
}

Dart_Handle DartPersistentValue::Release() {
  Dart_Handle local = Get();
  Clear();
  return local;
}

通过callback.Release()拿到Dart_Handle也就是原来Dart端设置的callback对应的指针,然后使用tonic::DartInvoke去调用这个函数

Dart_Handle DartInvoke(Dart_Handle closure,
                       std::initializer_list<Dart_Handle> args) {
  int argc = args.size();
  Dart_Handle* argv = const_cast<Dart_Handle*>(args.begin());
  Dart_Handle handle = Dart_InvokeClosure(closure, argc, argv);
  LogIfError(handle);
  return handle;
}

最后通过Dart_InvokeClosure去调用这个Dart方法,最后面会回到Dart函数的callback里面,代码我们再贴一遍

Future<ByteData?> _sendPlatformMessage(String channel, ByteData? message) {
  final Completer<ByteData?> completer = Completer<ByteData?>();
  // ui.PlatformDispatcher.instance is accessed directly instead of using
  // ServicesBinding.instance.platformDispatcher 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.PlatformDispatcher.instance because the PlatformDispatcher may be
  // dependency injected elsewhere with a different instance. However, static
  // access at this location seems to be the least bad option.
  ui.PlatformDispatcher.instance.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;
}

这里通过completer.complete(reply)函数去返回函数send调用者使用await函数等待的future

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

然后返回值通过codec解码后返回即可,好了到这里我们从Dart端调用到Android端以及其相关的回调我们已经介绍完毕了,我们再来从Android端看看他与Dart通信是怎么样的呢

Android端向Dart端发送消息:

由于原理相同我们只介绍BasicMessageChannel,首先Android端发送消息一般是通过BasicMessageChannel的send方法

@UiThread
public void send(@Nullable T message, @Nullable final Reply<T> callback) {
  messenger.send(
      name,
      codec.encodeMessage(message),
      callback == null ? null : new IncomingReplyHandler(callback));
}

public interface Reply<T> {
  /**
   * Handles the specified message reply.
   *
   * @param reply the reply, possibly null.
   */
  void reply(@Nullable T reply);
}

Message是消息内容,Reply是一个函数式接口作为回调,里面把消息经过Codec加密过后通过messenger.send继续处理发送,上面说过messenger在Android端默认是DartExecutor

DartExecutor.send

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

DartMessenger.send

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

看到发送的之前做了一些处理,pendingReplies存的是回调的replyId以及对应的CallBack,send调用方法的时候先把replyId设置一个唯一的整数绑定CallBack,然后把需要回调的replyId传给调用端,然后等调用端回复的时候再把replyId传回来找到对应的Callback调用其回调,方法还是很巧妙的,这样设计双方只是在传递一个的Id而已效率还是比较高的(可以看见回调的处理方式Dart端与Android端基本思路是一样的);我们再来看看发送,发送是通过flutterJNI.dispatchPlatformMessage进行的,我们具体看看他

/** Sends a reply {@code message} from Android to Flutter over the given {@code channel}. */
@UiThread
public void dispatchPlatformMessage(
    @NonNull String channel, @Nullable ByteBuffer message, int position, int responseId) {
  ensureRunningOnMainThread();
  if (isAttached()) {
    nativeDispatchPlatformMessage(nativeShellHolderId, channel, message, position, responseId);
  } else {
    Log.w(
        TAG,
        "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: "
            + channel
            + ". Response ID: "
            + responseId);
  }
}

nativeDispatchPlatformMessage是一个C++的方法,从这里以后Android会转换为C++层来执行

{
    .name = "nativeDispatchPlatformMessage",
    .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V",
    .fnPtr = reinterpret_cast<void*>(&DispatchPlatformMessage),
}

C++ 把这个JAVA方法调用映射成了DispatchPlatformMessage的C++方法

static void DispatchPlatformMessage(JNIEnv* env,
                                    jobject jcaller,
                                    jlong shell_holder,
                                    jstring channel,
                                    jobject message,
                                    jint position,
                                    jint responseId) {
  ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPlatformMessage(
      env,                                         //
      fml::jni::JavaStringToString(env, channel),  //
      message,                                     //
      position,                                    //
      responseId                                   //
  );
}

void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env,
                                                  std::string name,
                                                  jobject java_message_data,
                                                  jint java_message_position,
                                                  jint response_id) {
  uint8_t* message_data =
      static_cast<uint8_t*>(env->GetDirectBufferAddress(java_message_data));
  fml::MallocMapping message =
      fml::MallocMapping::Copy(message_data, java_message_position);

  fml::RefPtr<flutter::PlatformMessageResponse> response;
  if (response_id) {
    response = fml::MakeRefCounted<PlatformMessageResponseAndroid>(
        response_id, jni_facade_, task_runners_.GetPlatformTaskRunner());
  }

  PlatformView::DispatchPlatformMessage(
      std::make_unique<flutter::PlatformMessage>(
          std::move(name), std::move(message), std::move(response)));
}

我们再往下看看这个方法,这里把调用的name,message以及response封装成一个PlatformMessage用于往下的函数调用传递,这个与Dart调用Android里面的这个步骤看起来是一样的,值得一提的是这里把response_id,jni_facade_,task_runners_.GetPlatformTaskRunner封装成一个PlatformMessageResponseAndroid对象用于参数的传递,这里与Dart调用Android思路也是一致的


PlatformMessageResponseAndroid::PlatformMessageResponseAndroid(
    int response_id,
    std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
    fml::RefPtr<fml::TaskRunner> platform_task_runner)
    : response_id_(response_id),
      jni_facade_(jni_facade),
      platform_task_runner_(std::move(platform_task_runner)) {}

PlatformMessageResponseAndroid::~PlatformMessageResponseAndroid() = default;

// |flutter::PlatformMessageResponse|
void PlatformMessageResponseAndroid::Complete(
    std::unique_ptr<fml::Mapping> data) {
  platform_task_runner_->PostTask(
      fml::MakeCopyable([response_id = response_id_,  //
                         data = std::move(data),      //
                         jni_facade = jni_facade_]() mutable {
        jni_facade->FlutterViewHandlePlatformMessageResponse(response_id,
                                                             std::move(data));
      }));
}

我们接着往下看最后面会到PlatformConfiguration的DispatchPlatformMessage方法:

void PlatformConfiguration::DispatchPlatformMessage(
    std::unique_ptr<PlatformMessage> message) {
  std::shared_ptr<tonic::DartState> dart_state =
      dispatch_platform_message_.dart_state().lock();
  if (!dart_state) {
    FML_DLOG(WARNING)
        << "Dropping platform message for lack of DartState on channel: "
        << message->channel();
    return;
  }
  tonic::DartState::Scope scope(dart_state);
  Dart_Handle data_handle =
      (message->hasData()) ? ToByteData(message->data()) : Dart_Null();
  if (Dart_IsError(data_handle)) {
    FML_DLOG(WARNING)
        << "Dropping platform message because of a Dart error on channel: "
        << message->channel();
    return;
  }

  int response_id = 0;
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    pending_responses_[response_id] = response;
  }

  tonic::LogIfError(
      tonic::DartInvoke(dispatch_platform_message_.Get(),
                        {tonic::ToDart(message->channel()), data_handle,
                         tonic::ToDart(response_id)}));
}

代码片段


Dart_Handle library = Dart_LookupLibrary(tonic::ToDart("dart:ui"));

dispatch_platform_message_.Set(
    tonic::DartState::Current(),
    Dart_GetField(library, tonic::ToDart("_dispatchPlatformMessage")));

最后面通过dispatch_platform_message_.Get()得到了hook.dart的_dispatchPlatformMessage方法引用,并且通过tonic::DartInvoke去调用这个Dart方法,并且把参数传过去

Dart_Handle DartInvoke(Dart_Handle closure,
                       std::initializer_list<Dart_Handle> args) {
  int argc = args.size();
  Dart_Handle* argv = const_cast<Dart_Handle*>(args.begin());
  Dart_Handle handle = Dart_InvokeClosure(closure, argc, argv);
  LogIfError(handle);
  return handle;
}

C++里面的DartInvoke里面通过Dart_InvokeClosure去调用Dart方法,但是Dart_InvokeClosure目前源码已经查找不到了,没有继续跟踪了C++到底是怎么调用到Dart方法的了,让我们再看看上面提到的hook.dart的_dispatchPlatformMessage方法:

@pragma('vm:entry-point')
void _dispatchPlatformMessage(String name, ByteData? data, int responseId) {
  PlatformDispatcher.instance._dispatchPlatformMessage(name, data, responseId);
}

PlatformDispatcher._dispatchPlatformMessage


void _dispatchPlatformMessage(String name, ByteData? data, int responseId) {
  if (name == ChannelBuffers.kControlChannelName) {
    try {
      channelBuffers.handleMessage(data!);
    } finally {
      _respondToPlatformMessage(responseId, null);
    }
  } else if (onPlatformMessage != null) {
    _invoke3<String, ByteData?, PlatformMessageResponseCallback>(
      onPlatformMessage,
      _onPlatformMessageZone,
      name,
      data,
      (ByteData? responseData) {
        _respondToPlatformMessage(responseId, responseData);
      },
    );
  } else {
    channelBuffers.push(name, data, (ByteData? responseData) {
      _respondToPlatformMessage(responseId, responseData);
    });
  }
}

void _invoke3<A1, A2, A3>(void Function(A1 a1, A2 a2, A3 a3)? callback, Zone zone, A1 arg1, A2 arg2, A3 arg3) {
  if (callback == null) {
    return;
  }

  assert(zone != null);

  if (identical(zone, Zone.current)) {
    callback(arg1, arg2, arg3);
  } else {
    zone.runGuarded(() {
      callback(arg1, arg2, arg3);
    });
  }
}

_invoke3会调用参数传过来的callback方法,并且把后面几个参数当做他的参数传进去,所以重点看看这个callback是什么,也就是上面的onPlatformMessage是什么,因为调用栈比较多我就不贴代码了他其实就是

代码片段

window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;

_defaultBinaryMessenger = createBinaryMessenger();

BinaryMessenger createBinaryMessenger() {
  return const _DefaultBinaryMessenger._();
}

所以onPlatformMessage就是运行_DefaultBinaryMessenger.handlePlatformMessage方法:

@override
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);
    } else {
      ui.channelBuffers.push(channel, data, callback!);
      callback = null;
    }
  } catch (exception, stack) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'services library',
      context: ErrorDescription('during a platform message callback'),
    ));
  } finally {
    if (callback != null) {
      callback(response);
    }
  }
}

这里_handlers[channel]通过channel名字取出来了MessageHandler handler,然后进行handler调用,这个_handlers[channel]是什么时候存放进去的呢,上面已经说过了是在setMessageHandler时候放进去的,大家可以往上翻翻,最后面我们再来说说回调是怎么处理的

Android端向Dart端发送消息以后Android端的回调处理:

大家应该还记得我们Android端调用Dart的时候是可以设置回调函数的,finally里面通过callback(response)调用(response是上面handler调用的返回值),而callback则是_dispatchPlatformMessage里面的_respondToPlatformMessage方法,responseId是调用前传进来的ID,responseData就是调用的返回值

_invoke3<String, ByteData?, PlatformMessageResponseCallback>(
      onPlatformMessage,
      _onPlatformMessageZone,
      name,
      data,
      (ByteData? responseData) {
        _respondToPlatformMessage(responseId, responseData);
      },
    );
/// Called by [_dispatchPlatformMessage].
void _respondToPlatformMessage(int responseId, ByteData? data)
    native 'PlatformConfiguration_respondToPlatformMessage';

_respondToPlatformMessage是一个C++的方法,这里又回到了C++层了,看看他对应的是哪个C++方法

{"PlatformConfiguration_respondToPlatformMessage",
 _RespondToPlatformMessage, 3, true},

代码片段对应的是_RespondToPlatformMessage方法

void _RespondToPlatformMessage(Dart_NativeArguments args) {
  tonic::DartCallStatic(&RespondToPlatformMessage, args);
}

void RespondToPlatformMessage(Dart_Handle window,
                              int response_id,
                              const tonic::DartByteData& data) {
  if (Dart_IsNull(data.dart_handle())) {
    UIDartState::Current()
        ->platform_configuration()
        ->CompletePlatformMessageEmptyResponse(response_id);
  } else {
    // TODO(engine): Avoid this copy.
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
    UIDartState::Current()
        ->platform_configuration()
        ->CompletePlatformMessageResponse(
            response_id,
            std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()));
  }
}

最后面会调用CompletePlatformMessageResponse方法回调到Android端

void PlatformConfiguration::CompletePlatformMessageResponse(
    int response_id,
    std::vector<uint8_t> data) {
  if (!response_id) {
    return;
  }
  auto it = pending_responses_.find(response_id);
  if (it == pending_responses_.end()) {
    return;
  }
  auto response = std::move(it->second);
  pending_responses_.erase(it);
  response->Complete(std::make_unique<fml::DataMapping>(std::move(data)));
}

先从pending_responses_按照response_id取出response,我们从Android调用Dart的时候设置过这个值,他的对象为PlatformMessageResponseAndroid,去看看的Complete方法:

// |flutter::PlatformMessageResponse|
void PlatformMessageResponseAndroid::Complete(
    std::unique_ptr<fml::Mapping> data) {
  platform_task_runner_->PostTask(
      fml::MakeCopyable([response_id = response_id_,  //
                         data = std::move(data),      //
                         jni_facade = jni_facade_]() mutable {
        jni_facade->FlutterViewHandlePlatformMessageResponse(response_id,
                                                             std::move(data));
      }));
}

准备通过JNI再回到Android端

void PlatformViewAndroidJNIImpl::FlutterViewHandlePlatformMessageResponse(
    int responseId,
    std::unique_ptr<fml::Mapping> data) {
  // We are on the platform thread. Attempt to get the strong reference to
  // the Java object.
  JNIEnv* env = fml::jni::AttachCurrentThread();

  auto java_object = 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;
  }
  if (data == nullptr) {  // Empty response.
    env->CallVoidMethod(java_object.obj(),
                        g_handle_platform_message_response_method, responseId,
                        nullptr);
  } else {
    // Convert the vector to a Java byte array.
    fml::jni::ScopedJavaLocalRef<jobject> data_array(
        env, env->NewDirectByteBuffer(const_cast<uint8_t*>(data->GetMapping()),
                                      data->GetSize()));

    env->CallVoidMethod(java_object.obj(),
                        g_handle_platform_message_response_method, responseId,
                        data_array.obj());
  }

  FML_CHECK(fml::jni::CheckException(env));
}


g_handle_platform_message_response_method = env->GetMethodID(
    g_flutter_jni_class->obj(), "handlePlatformMessageResponse",
    "(ILjava/nio/ByteBuffer;)V");

g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
    env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));

关键的代码片段我贴出来了,一目了然的调用到了FlutterJNI的handlePlatformMessageResponse方法,最后再看看他

// Called by native to respond to a platform message that we sent.
// TODO(mattcarroll): determine if reply is nonull or nullable
@SuppressWarnings("unused")
private void handlePlatformMessageResponse(int replyId, byte[] reply) {
  if (platformMessageHandler != null) {
    platformMessageHandler.handlePlatformMessageResponse(replyId, reply);
  }
  // TODO(mattcarroll): log dropped messages when in debug mode
  // (https://github.com/flutter/flutter/issues/25391)
}

platformMessageHandler 其实就是 DartMessenger 上面已经说过了

@Override
public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
  Log.v(TAG, "Received message reply from Dart.");
  BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId);
  if (callback != null) {
    try {
      Log.v(TAG, "Invoking registered callback for reply from Dart.");
      callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
    } catch (Exception ex) {
      Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
    } catch (Error err) {
      handleError(err);
    }
  }
}

这里大家应该很熟悉了通过pendingReplies.remove取出了callback,然后进行调用即可,至于什么时候存放的环节大家往上翻翻就明白了是在send的时候就已经注册了,好了到这里Android调用Dart以及Dart回调Android整个步骤我们已经也介绍完了

最后想说的话:

如果大家仔细看完的话我相信你对于整个流程的调用已经明白了,本着通俗易懂的原则我算是用大白话以及边附上代码边解释的方式带领大家去理解了,相信你如果从来都没了解过Flutter底层通信的也能很容易的去了解到他的原理以及步骤,顺便提一句如果你能一口气看到这里并且内容大致都能够理解的话那么我觉得你的耐心还是不错的呢,我写到这里确实写了很久,😁最后希望大家关注我,并且留言或给个点赞吧,你的关注是我持续写作的动力,下一篇我们会介绍从Platform到Dart通信原理分析之iOS篇(我是不会抛弃iOS小伙伴的,😁谁让我是全栈呢),小伙伴们一起期待一下吧···

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,540评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,640评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,643评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,672评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,525评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,387评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,809评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,450评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,743评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,787评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,560评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,406评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,824评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,048评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,335评论 1 253
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,784评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,997评论 2 337

推荐阅读更多精彩内容