之前我们介绍了如何在 Native (Android 项目) 中启动 Flutter,展示 Flutter 页面。但是在开发过程中,很多时候并不是简单的展示一个页面即可,还会涉及到各种交互,比如传递一些消息。
本篇文章就简单介绍一下 Flutter 与原生 Native 的三种交互方式:
- BasicMessageChannel
- MethodChannel
- EventChannel
BasicMessageChannel
虽然说是三种交互方式,但是其实本质都是一种,这个我们后面会解释。 先来看看 BasicMessageChannel。它可以实现双方交互,发送一些简单消息,消息类型 Object,但是并不是所有 Object 都可以,基础类型及基础类型的数组、list、map 是可以的。这个可以参考 BasicMessageChannel 的源码:
public void send(@Nullable T message, @Nullable final Reply<T> callback) {
messenger.send(
name,
codec.encodeMessage(message),
callback == null ? null : new IncomingReplyHandler(callback));
}
可以看到进行了 encode,这个 codec 一般是 StandardMessageCodec,它的 encodeMessage 函数源码:
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;
}
这里 writeValue 的源码:
protected void writeValue(ByteArrayOutputStream stream, Object value) { if (value == null || value.equals(null)) { stream.write(NULL); } else if (value == Boolean.TRUE) { stream.write(TRUE); } else if (value == Boolean.FALSE) { stream.write(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); } }
下面看一下如何来使用它,以 Android 端为例。
(1) 不使用 engine cache 预热
如果不使用 engine cache,那么在 FlutterActivity 的继承类中重写 configureFlutterEngine:
class MainActivity : FlutterActivity() { var channel : BasicMessageChannel? = null override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) var channel = BasicMessageChannel<String>(flutterEngine.dartExecutor.binaryMessenger,"test" ,StringCodec.INSTANCE) channel.setMessageHandler { message, reply -> Log.e("recieve", message) } }}
注意这里第二个参数 "test" 是这通道 (channel) 的名称,两边名称一致才能进行通信。
第三个参数是消息的编解码器,这里我们因为是简单的示例,消息是字符串 String,所以用 StringCodec。 StringCodec 是 MessageCodec 接口的实现,除了它还有 BinaryCodec,JsonMessageCodec,StandardMessageCodec。另外我们还可以自己实现 MessageCodec,实现它的两个函数即可,它的源码如下:
public interface MessageCodec<T> { /** * Encodes the specified message into binary. * * @param message the T message, possibly null. * @return a ByteBuffer containing the encoding between position 0 and the current position, or * null, if message is null. */ @Nullable ByteBuffer encodeMessage(@Nullable T message);
/** * Decodes the specified message from binary. * * @param message the {@link ByteBuffer} message, possibly null. * @return a T value representation of the bytes between the given buffer's current position and * its limit, or null, if message is null. */ @Nullable T decodeMessage(@Nullable ByteBuffer message);}
最后,MessageHandler 用于接受从 Flutter 传递过来的消息。这里简单的将消息打印出来。 当需要向 Flutter 发送消息时,执行以下代码即可:
channel?.send("android call")
(2) 使用 engine cache 预热
一般情况我们在 Application 中添加 cache,如下:
class App : Application() { companion object{ ... lateinit var flutterEngine2 : FlutterEngine } override fun onCreate() { super.onCreate() ...
flutterEngine2 = FlutterEngine(this) flutterEngine2.navigationChannel.setInitialRoute("second") flutterEngine2.dartExecutor.executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) FlutterEngineCache.getInstance().put("second", flutterEngine2) }}
这里我们为 second 这个 Flutter 页面创建 engine 并加入 cache 进行预热。
如果我们想使用这个 engine 发送消息,那么可以直接创建 BasicMessageChanne
var channel = BasicMessageChannel<String>(App.flutterEngine2.dartExecutor.binaryMessenger,"test" ,StandardMessageCodec.INSTANCE as MessageCodec<String>)channel.setMessageHandler { message, reply -> Log.e("recieve", message)}
后续与上面就一样了。
Flutter 端
步骤基本一样,先创建
static const messageChannel = const BasicMessageChannel("test", StringCodec());
这里通道名称保持与 native 一致。
设置回调:
messageChannel.setMessageHandler((message) async { print(message) } );
发送消息:
messageChannel.send("flutter call");
MethodChannel
用于双方函数的调用,使用方法与 BasicMessageChannel 相似,其实本质上是一样的。我们先来看看如何使用它。
Android 端****
与 BasicMessageChannel 一样预热和不预热可以有两种不同的处理,但是其实最终都是获取到 FlutterEngine 对象,所以就不赘述了,直接使用即可。代码如下:
//创建 var channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"test") //回调,根据call执行native函数 channel.setMethodCallHandler { call, result -> when(call.method){ "flutterCall" -> { //执行我们自定义的对应函数 flutterCall(call.arguments) } else -> {} } }
这里 FlutterCall 是响应 Flutter 发送过来的请求,我们定义一个对应的函数来处理,如:
fun flutterCall(arguments : Object){ Log.e("flutterCall", "message:" + arguments.toString()) }
然后我们可以通过 invokeMethod 函数来执行 Flutter 函数,如:
//执行flutter函数 channel.invokeMethod("androidCall", "android message")
Flutter 端
流程一样,代码如下:
//创建static const methodChannel = const MethodChannel("test");//回调,根据call执行flutter函数 methodChannel.setMethodCallHandler((call) async { switch(call.method){ case "androidCall": //执行自定义的对应函数 androidCall(call.arguments); break; } });//执行native函数methodChannel.invokeMethod("flutterCall", "flutter message");
源码分析 ****在分析 BasicMessageChannel 时我们知道它的 send 函数其实是调用了 messenger.send(...),这个 messenger 是 BinaryMessenger,就是构造函数的第一个参数。MethodCannel 也是一样,它的 invokeMethod 函数源码如下:
@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)); }
可以看到,最终还是调用了 BinaryMessenger 的 send 函数。只不过将 invokeMethod 的两个参数 (String 类型的函数名 method 和 Object 类型的参数 arguments) 封装到 MethodCall 中。
再来看回调的处理,上面 invokeMethod 函数中可以看到,用 IncomingResultHandler 将 callback 进行了封装,它的关键源码如下:
private final class IncomingMethodCallHandler implements BinaryMessageHandler { private final MethodCallHandler handler;
IncomingMethodCallHandler(MethodCallHandler handler) { this.handler = handler; }
@Override @UiThread public void onMessage(ByteBuffer message, final BinaryReply reply) { final MethodCall call = codec.decodeMethodCall(message); try { handler.onMethodCall( call, new Result() { ... }); } catch (RuntimeException e) { ... } } ... }
可以看到在收到消息 onMessage 后先将消息解析成 MethodCall 在执行 callback,这样就可以直接获取到函数名及参数了。\
通过上面我们知道 MethodChannel 和 BasicMessageChannel 本质是一样的,只不过经过了一层 MethodCall 的封装,方便直接获取函数名和参数。\
EventChannel
EventChannel 与上面两个都不太一样,它是 Flutter 发起,native 处理并返回结果,Flutter 再处理结果。说它是单方向通道也不是很准确,但是 native 无法主动发起,所以更像是一个 c/s 结构。先来看看如何使用。
Android 端
同样需要 FlutterEngine 对象,代码如下:
//创建var channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger,"test")//设置处理handlerchannel.setStreamHandler(object : StreamHandler(), EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { //根据arguments处理 arguments?.let { ... //将处理结果返回,可能成功也可能失败 events?.success("android back") //events?.error("errorcode", "errormssage", null) //如果不返回,即success和error都不执行,则需要执行endOfStream //events?.endOfStream() } }
override fun onCancel(arguments: Any?) { //执行取消操作 }})
上面提到 Native 无法主动发起,所以就没有类似上面 send 或 invokeMethod 函数。\
Flutter 端
通过 receiveBroadcastStream 来发送 event 请求,并通过 linsten 来监听返回。
//创建static const eventChannel = const EventChannel("test");//发送arguments给native处理,并监听结果eventChannel.receiveBroadcastStream(["flutter event"]).listen((event) { //返回成功结果,处理 print(event.toString());}, onError: (event){ //返回错误结果,处理}, onDone: (){ //执行完成处理});
源码分析
我们来看一下 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 { ... }); try { await methodChannel.invokeMethod<void>('listen', arguments); } catch (exception, stack) { ... } }, onCancel: () async { binaryMessenger.setMessageHandler(name, null); try { await methodChannel.invokeMethod<void>('cancel', arguments); } catch (exception, stack) { ... } }); return controller.stream; }
可以看到 EventChannel 本质上就是 MethodChannel,只不过执行了几个预先定义好的函数,如 listen 和 cancel。这样对 MethodChannel 进行再次封装,可以更简单的进行事件传递。
总结
上面我们展示了三种交互方式的使用,并解析了其内部的联系。其实可以看到三种方式最终其实都是使用了 BinaryMessenger 这一抽象类的默认实现 _DefaultBinaryMessenger。所以如果我们通过 BinaryMessenger 来实现一套自己特别的消息传递机制。