本文原作者: BennuC,原文发布于: BennuCTech
MethodChannel
EventChannel
BasicMessageChannel
public void send( T message, 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 端为例。
如果不使用 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.
*/
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.
*/
T decodeMessage(@Nullable ByteBuffer message);
}
最后,MessageHandler 用于接受从 Flutter 传递过来的消息。这里简单的将消息打印出来。
当需要向 Flutter 发送消息时,执行以下代码即可:
channel?.send("android call")
一般情况我们在 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 发送消息,那么可以直接创建 BasicMessageChannel
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");
这样就实现了 Native 和 Flutter 的双向消息交互。
MethodChannel
与 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")
//创建
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");
public void invokeMethod(String method, Object arguments, Result callback) {
messenger.send(
name,
codec.encodeMethodCall(new MethodCall(method, arguments)),
callback == null ? null : new IncomingResultHandler(callback));
}
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
private final MethodCallHandler handler;
IncomingMethodCallHandler(MethodCallHandler handler) {
this.handler = handler;
}
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
Android 端
同样需要 FlutterEngine 对象,代码如下:
//创建
var channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger,"test")
//设置处理handler
channel.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?) {
//执行取消操作
}
})
Flutter 端
//创建
static const eventChannel = const EventChannel("test");
//发送arguments给native处理,并监听结果
eventChannel.receiveBroadcastStream(["flutter event"]).listen((event) {
//返回成功结果,处理
print(event.toString());
}, onError: (event){
//返回错误结果,处理
}, onDone: (){
//执行完成处理
});
源码分析
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;
}
总结
长按右侧二维码
查看更多开发者精彩分享
"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。
点击屏末 | 阅读原文 | 即刻报名参与 "开发者说·DTalk"