一、前言
在 Flutter Platform Channel - MethodChannel 使用 一文中我们已经学习了 MethodChannel 如何使用,本篇文章我们将继续学习 BasicMessageChannel & EventChannel 如何使用,以及它们之间的区别。
二、BasicMessageChannel
本例我们将会通过 Flutter 端调用 Native 方法,然后 Native 返回给 Flutter 数据进行展示的案例来演示一下具体怎么使用。
1、Flutter 端
在上篇文章代码的基础上,我们继续编写 BasicMessageChannel 所需代码,首先修改 PluginTest 文件,修改如下:
const BasicMessageChannel<dynamic> basicMessageChannel =
BasicMessageChannel<dynamic>(
'flutter.billionbottle.com/basic-message-channel',
StandardMessageCodec(),
);
_singleton = PluginTest._(
methodChannel,
eventChannel,
basicMessageChannel,
);
PluginTest._(
this._methodChannel,
this._eventChanel,
this._basicMessageChannel,
);
final BasicMessageChannel<dynamic> _basicMessageChannel;
Future<Map<dynamic, dynamic>?> sendBasicMessage(
Map<String, dynamic> params,
) async {
final Map<dynamic, dynamic>? result =
await _basicMessageChannel.send(params);
return result;
}
- 参数类型任意,多个参数通常使用
Map; - 返回
Future,原生端返回的数据;
然后修改 PluginDemoPage 代码,如下:
String? basicMessageResult;
Future<void> sendBasicMessage() async {
final PluginTest plugin = PluginTest();
final Map<dynamic, dynamic>? result = await plugin.sendBasicMessage(
<String, dynamic>{
'name': 'tom',
'age': '20',
},
);
setState(() {
basicMessageResult = '$result';
});
}
OutlinedButton(
onPressed: sendBasicMessage,
child: Text('SendBasicMessage'),
),
Text('basicMessageResult 返回的数据:$basicMessageResult'),
2、Android 端
然后再新建一个 BasicMessageChannelTest 文件,编写如下代码:
class BasicMessageChannelTest(messenger: BinaryMessenger):BasicMessageChannel.MessageHandler<Any> {
private val channelName = "flutter.billionbottle.com/basic-message-channel"
private val channel: BasicMessageChannel<Any>
init {
channel = BasicMessageChannel(messenger, channelName, StandardMessageCodec())
channel.setMessageHandler(this)
}
override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any>) {
val name = (message as Map<String, Any>)["name"]
val age = (message as Map<String, Any>)["age"]
var result = mapOf("name" to name,"age" to age)
reply.reply(result)
}
}
最后在 MainActivity#configureFlutterEngine 中添加如下代码:
BasicMessageChannelTest(flutterEngine.dartExecutor.binaryMessenger)
message是传入的参数,由于 Flutter 端传入的是 Map,所以上面的解析按照 Map 解析;reply.reply()是返回给 Flutter 的结果;
此时运行程序我们可以下看到如下效果:
相关代码修改可以查看 Git 提交记录。
三、EventChannel 使用
本例我们将会通过 Flutter 端调用 Native 方法,然后 Native 返回给 Flutter 数据进行展示的案例来演示一下 EventChannel 的使用。
1、Flutter 端
我们在上述代码的基础上继续编写 EventChannel 所需代码,首先修改 PluginTest 文件,修改如下:
StreamSubscription<dynamic> onProgress(MethodCallback callback) {
return _eventChanel.receiveBroadcastStream().listen(callback);
}
然后再编写 PluginDemoPage 页面的代码,文件修改如下:
class PluginDemoPage extends StatefulWidget {
PluginDemoPage({super.key});
@override
State<PluginDemoPage> createState() => _PluginDemoPageState();
}
class _PluginDemoPageState extends State<PluginDemoPage> {
dynamic eventChannelResult;
StreamSubscription<dynamic>? subscription;
@override
void initState() {
super.initState();
final PluginTest plugin = PluginTest();
subscription = plugin.onProgress(_onData);
}
void _onData(dynamic event) {
setState(() {
eventChannelResult = event;
});
}
@override
void dispose() {
super.dispose();
subscription?.cancel();
}
@override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(
title: Text('plugin Demo'),
centerTitle: true,
),
body: Column(
children: <Widget>[
Text('eventChannel 返回的数据:$eventChannelResult'),
],
),
),
);
}
}
2、Android 端
新建 EventChannelTest 文件,代码如下:
class EventChannelTest(var activity: Activity,messenger: BinaryMessenger):EventChannel.StreamHandler {
private val channelName = "flutter.billionbottle.com/event-channel"
private val channel: EventChannel
private var events: EventChannel.EventSink? = null
private var eventChannelCount = 20
init {
channel = EventChannel(messenger, channelName)
channel.setStreamHandler(this)
starTimer()
}
private fun starTimer() {
var timer = Timer().schedule(timerTask {
activity.runOnUiThread {
var map = mapOf("eventChannelCount" to eventChannelCount++,"eventChannel" to "eventChannel")
events?.success(map)
}
}, 0, 1000)
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
this.events = events
}
override fun onCancel(arguments: Any?) {
this.events = null
}
}
然后修改 MainActivity#configureFlutterEngine代码如下:
EventChannelTest(this,flutterEngine.dartExecutor.binaryMessenger)
events?.success(map) 方法向 Flutter 端发送数据,此方法必须在主线程执行。
运行程序后,UI 效果如下:
相关代码修改可以查看 Git 提交记录。
四、源码分析
至此,我们已经学习了如何使用 BasicMessageChannel 和 MethodChannel,下面我们看下 Flutter 端它们的源码。
BasicMessageChannel 的源码如下:
通过源码我们可以看出 BasicMessageChannel 主要有一个构造函数,codec 和 name 两个变量和 一个 binaryMessenger getter 属性,剩下的就是 send 和 setMessageHandler 两个方法了。
MethodChannel 的源码如下:
通过它的源码我们可以看出,MethodChannel 的方法相对来说比较多。除了有 setMethodCallHandler,还有 invokeMethod、invokeListMethod、invokeMapMethod 等。不过仔细查看源码,我们发现这几个方法其实都是调用了 _invokeMethod 方法,而 _invokeMethod方法最终会调用 binaryMessenger.send 方法,其实就是跟 BasicMessageChannel#send 差不多。
BasicMessageChannel#send 方法如下:
Future<T?> send(T message) async {
return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
}
MethodChannel#_invokeMethod 方法如下:
Future<T?> _invokeMethod<T>(String method, { required bool missingOk, dynamic arguments }) async {
assert(method != null);
final ByteData? result = await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
if (missingOk) {
return null;
}
throw MissingPluginException('No implementation found for method $method on channel $name');
}
return codec.decodeEnvelope(result) as T?;
}
主要是把方法名和参数通过 codec 转化为二进制数据,通过 BinaryMessages send 出去,并等待这个方法返回 result。
其实看完他们的源码就会发现 MethodChannel 其实就是为了方便使用对 BasicMessageChannel 进行的一层封装而已。
EventChannel 源码如下:
通过源码可以发现,EventChannel 其实是基于 MethodChannel 来实现的。
发送消息会对消息进行编码解码,我们来看下有哪些编解码类型:
BinaryCodec发送二进制消息时;JSONMessageCodec发送Json格式消息时;StandardMessageCodec发送基本型数据时;StringCodec发送String类型消息时;
大部分情况下我们使用 StandardMessageCodec 类型就可以了,MethodChannel 中默认 codec 就是 StandardMethodCodec,当然我们也可以根据自己的需求选择不同的 codec。