一、通信方式介绍
-
MethodChannel
与原生互相进行方法调用,用于方法调用(双向) -
BasicMessageChannel
与原生互相发送消息,用于数据传输(双向) -
EventChannel
原生发送消息,Flutter 接收,用于数据流通信(单向)
二、三种方式使用对比
以下主要针对 MethodChannel
这种常用通信方式做详细分析。
三、MethodChannel 通信类设计
-
以下为 Android 端类图,对于 Dart 侧,基本一致,不细述
关于 MethodChannel 方式相互调用的流程简图如下:
- invokeMethod() 发送消息,将 MethodCall 进行编码传输,Native 端是编码为
ByteBuffer
,Dart 侧则是编码为ByteData
; - Native 侧收到 Dart 消息后,底层通过 FlutterJNI 实现,上层通过
BinaryMessageHandler.onMessage()
回调监听,将收到的ByteBuffer
解码为 MethodCall ; - Native 根据方法传参做逻辑处理后,若要回复消息/回传返回值,则通过
BinaryReply result
进行,最终传输的数据会被编码为ByteBuffer
回信过去,方法也是表达得极为明确:encodeEnvelope()
回信。
消息的编码与解码 MessageCodec
,Dart 和 native 侧分别有四种编码方式,两端都是一致的。
备注:点击查看 类型对应关系
四、MethodChannel 方法调用示例
无论是 Dart 调用 Android,还是 Android 调用 Dart,均使用 invokeMethod()
触发调用 以及 setMethodCallHandler()
监听调用事件进行。
1. Dart 调用 Native 方法
1)调用 Native 方法getMessage
:MethodChannel.invokeMethod('getMessage')
2)Native 监听方法调用并执行对应方法 Native 逻辑
3)如果需要回传 Native 数据给 Dart,则使用:result.success() 传递数据,该结果直接从上述 invokeMethod()
方法可获取到。
class MainActivity: FlutterActivity() {
val CHANNEL = "native.call";
var count = 0;
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
// Note: this method is invoked on the main thread.
if (call.method == "showToast") {
Toast.makeText(this, "Hello flutter, I'm Android !", Toast.LENGTH_LONG).show();
} else if (call.method == "getMessage") {
// 一次会话通道只能回复一次,不能回复多次,否则抛异常:java.lang.IllegalStateException: Reply already submitted
result.success("Android: ${count++}")
}
Log.e("DartCall", Thread.currentThread().name)
for (i in 0..5) {
channel.invokeMethod("callDart", "Hell dart, $i")
}
}
}
}
2. Native 调用 Dart 方法
- 在 initState() 方法中,注册 native 调用 dart 方法监听:
platform.setMethodCallHandler((MethodCall call) { }
- 处理 native 方法传参。
// 1. Android 调用 Dart 方法:
for (i in 0..5) {
channel.invokeMethod("callDart", "Hell dart, $i")
}
// 2. Dart 侧注册 Native 事件调用监听
@override
void initState() {
super.initState();
registerNativeCall();
}
dynamic nativeArgs;
void setNativeArgs(dynamic args) {
nativeArgs = args;
print('$nativeArgs');
}
void registerNativeCall() {
platform.setMethodCallHandler((MethodCall call) {
if (call.method == 'callDart') {
setNativeArgs(call.arguments);
}
});
}
3. 注意事项
1)Dart 调用 Native 方法,Native 侧可以直接使用 MethodChannel.Result
回传数据给 Dart,但是一次通信过程中,result.success()
方法只能调用一次,否则就会出现以下异常:
E/DartMessenger(19357): Uncaught exception in binary message listener
E/DartMessenger(19357): java.lang.IllegalStateException: Reply already submitted
E/DartMessenger(19357): at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:149)
E/DartMessenger(19357): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:253)
E/DartMessenger(19357): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:91)
E/DartMessenger(19357): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:724)
E/DartMessenger(19357): at android.os.MessageQueue.nativePollOnce(Native Method)
E/DartMessenger(19357): at android.os.MessageQueue.next(MessageQueue.java:326)
E/DartMessenger(19357): at android.os.Looper.loop(Looper.java:160)
E/DartMessenger(19357): at android.app.ActivityThread.main(ActivityThread.java:6898)
E/DartMessenger(19357): at java.lang.reflect.Method.invoke(Native Method)
E/DartMessenger(19357): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
E/DartMessenger(19357): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
解决方法,如果要持续传递 Native 数据给 Dart,则直接使用 invokeMethod()
方法,Dart 侧监听到自己方法调用,再做处理。
4. 完整示例
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NativeCallDemoWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() => NativeCallState();
}
class NativeCallState extends State<NativeCallDemoWidget> {
static const platform = MethodChannel('native.call');
String nativeMessage = 'Hello: ';
Future<void> showToast() async {
await platform.invokeMethod('showToast');
}
Future<String> getMessage() async {
String result = await platform.invokeMethod('getMessage');
// 更新页面数据
setState(() {
nativeMessage += result;
});
return result;
}
dynamic nativeArgs;
void setNativeArgs(dynamic args) {
nativeArgs = args;
print('$nativeArgs');
}
void registerNativeCall() {
platform.setMethodCallHandler((MethodCall call) {
if (call.method == 'callDart') {
setNativeArgs(call.arguments);
}
});
}
@override
void initState() {
super.initState();
registerNativeCall();
}
@override
Widget build(BuildContext context) {
Widget body = Column(
children: [
FlatButton(
child: Text('Show Native Toast'),
onPressed: () {
showToast();
},
shape: RoundedRectangleBorder(
side: BorderSide()
),
),
FlatButton(
child: Text('getMessage from Native'),
onPressed: () {
getMessage();
},
shape: RoundedRectangleBorder(
side: BorderSide()
),
),
Text(nativeMessage)
],
);
return new MaterialApp(
title: 'Flutter 与 Native 通信方式',
theme: new ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.blue
),
home: new Scaffold(
appBar: new AppBar(
title: new Text(
'Flutter 与 Native 通信方式'
),
),
body: body,
),
);
}
}
六、相关文档
- 工匠若水 - Channel 源码解析:https://blog.csdn.net/yanbober/article/details/119521158?spm=1001.2014.3001.5501