Flutter定义了三种不同类型的Channel,分别用于传递字符串和半结构化信息的BasicMessageChannel、用于传递方法调用(method invocation)的MethodChannel以及用于数据流(event streams)通信的EventChannel,如图所示:

三种Channel之间相互独立,各有用途,但在设计上非常相似。每种Channel 均有三个重要的成员变量:String 类型的 name,代表 Channel 的名字,也是其唯一的标识符;BinaryMessenger 类型的 messager,代表消息信使,是消息的发送与接收的工具;MessageCodec 类型或 MethodCodec 类型的 Codec,代表消息的编解码器。
它们的通信工具都是BinaryMessager。BinaryMessenger是Platform端与Flutter端通信的工具,其通信使用的消息格式为二进制格式数据。当初始化一个 Channel,并向该 Channel注册处理消息的 Handler 时,实际上会生成一个与之对应的 BinaryMessageHandler,并 以 channel name 为 Key,注册到 BinaryMessenger 中。 当Flutter端将消息发送到BinaryMessenger时,BinaryMessenger会根据其入参Channel找到对应的BinaryMessageHandler,并交由其处理。
BinaryMessenger 并不知道 Channel 的 存 在, 它 只 和 BinaryMessageHandler 打交 道。 而 Channel 和 BinaryMessageHandler 则 是 一 一 对 应 的。由 于 Channel 从 BinaryMessageHandler 接收到的消息是二进制格式数据,无法直接使用,故 Channel 会将该二进制消息通过 Codec(消息编解码器)解码为能识别的消息,并传递给 Handler 处理。
当 Handler 处理完消息之后,会通过回调函数返回 result,并将 result 通过编解码器编码为二进制格式数据,通过 BinaryMessenger 发送回 Flutter 端。
另外,在使用 Platform Channel 时,还需要牢记以下两点:
Platform 侧的代码运行在主线程。Flutter Engine 自己不创建线程,其线程的创建与管理是由 Embedder 提供的,并且 Flutter Engine 要求 Embedder 提供四个 Task Runner,分别是 Platform Task Runner、UI Task Runner、GPU Task Runner和 I/O Task Runner。Platform 侧执行的代码运行在 Platform Task Runner 中, 而在Flutter App 侧的代码运行在 UI Task Runner 中。在 Android 端和 iOS 端上,Platform Task Runner 运行在主线程上。因此,不应该在 Platform 端的 Handler 中处理耗时操作Platform Channel 并非是线程安全的。这一点在 Flutter 官方文档中也有提及。FlutterEngine 中的多个组件是非线程安全的,故与 FlutterEngine 的所有交互(接口调用)必须发生在 Platform Thread。故在将 Platform 端的消息处理结果回传到Flutter 端时,需要确保回调函数是在 Platform Thread(也就是 Android 和 iOS 的主线程)中执行的。BasicMessageChannel主要用于传递字符串和半结构化信息。
class MyFlutterActivity : FlutterActivity() {
companion object{
fun startActivity(activity: Activity){
val intent = Intent(activity,MyFlutterActivity::class.java)
activity.startActivity(intent)
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
val channel = BasicMessageChannel(
flutterEngine.dartExecutor.binaryMessenger,
"myflutter/testbasicmessagechannel",
JSONMessageCodec.INSTANCE
)
channel.setMessageHandler { message, reply ->
Log.d("MessageChannelTest", "in android Received message = $message")
reply.reply("Reply from Android")
}
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class BasicMessageChannelPage extends StatefulWidget{
@override
State<StatefulWidget> createState() => _BasicMessageChannelPage();
}
class _BasicMessageChannelPage extends State<BasicMessageChannelPage>{
static const _channel = BasicMessageChannel("myflutter/testbasicmessagechannel", JSONMessageCodec());
int i = 0;
void _sendMessage() async{
final String reply = await _channel.send('hello world $i') as String;
print('MessageChannelTest in dart $reply');
setState(() {
i++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BasicMessageChannelPage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("You have pushed the button this many times:"),
Text(
'$i',
style: Theme.of(context).textTheme.headline4,
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed:_sendMessage,
tooltip: 'Increment',
child: Icon(Icons.add),
), //
);
}
}
运行结果:

class MyFlutterActivity : FlutterActivity() {
companion object {
fun startActivity(activity: Activity) {
val intent = Intent(activity, MyFlutterActivity::class.java)
activity.startActivity(intent)
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
val methodChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"methodchannel"
)
methodChannel.setMethodCallHandler { call, result ->
if (call.method == "flutterMethod"){
Log.d("flutterMethod", "flutter调用native方法")
result.success("native传递数据到flutter")
}
}
}
void getNativeMethod() async{
String result = await _platform.invokeMethod("flutterMethod");
print("flutterMethod:$result");
}
结果:

native调用Flutter方法,如果在FlutterActivity里面调用Flutter页面的方法,此时是无法调用到的,因为Flutter页面还没初始化,没加载进来。所以如果要在native的FlutterActivity里面加载Flutter页面里面的方法,则需要保证Flutter页面已经初始化且加载了。可以考虑:
以下是Flutter调用原生,并在原生里面调用flutter,此时flutter一定已经初始化并加载了。
完整代码请看原生Android调用Flutter的dart方法
待续…