文章目录
1 FlutterChannel原理
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
的主线程)中执行的。
2 FlutterChannel使用
2.1 BasicMessageChannel使用
BasicMessageChannel主要用于传递字符串和半结构化信息。如果需要监听消息,则调用setMessageHandler
,如果要发送消息,则调用send
2.1.1 Flutter发送消息给原生
- 在FlutterActivity中创建BasicMessageChannel对象,并通过该对象与Flutter侧交互
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")
}
}
}
- 在Flutter侧创建BasicMessageChannel对象,并通过该对象调用send向原生发送数据,并通过它的返回值获取native原生那边返回的数据。
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),
), //
);
}
}
运行结果:
2.1.1 原生发送消息给Flutter
原生端代码:
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
)
//发送数据
mChannel.send("dddddd");
}
}
Flutter端
static const _channel = BasicMessageChannel("myflutterfragment/searchscandata", StandardMessageCodec());
_channel.setMessageHandler((message){
print('$message')
return Future(() => "");
});
2.2 MethodChannel使用
2.2.1 Flutter调用Native方法
- 在MyFlutterActivity中创建一个MethodChannel对象,然后通过setMethodCallHandler来处理flutter端调用请求。
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")
}
}
}
- 在flutter端创建MethodChannel,注意method名称和native那边的一致。
void getNativeMethod() async{
String result = await _platform.invokeMethod("flutterMethod");
print("flutterMethod:$result");
}
结果:
2.2.2 Native调用Flutter的方法
native调用Flutter方法,如果在FlutterActivity里面调用Flutter页面的方法,此时是无法调用到的,因为Flutter页面还没初始化,没加载进来。所以如果要在native的FlutterActivity里面加载Flutter页面里面的方法,则需要保证Flutter页面已经初始化且加载了。可以考虑:
- FlutterActivity页面延迟调用Flutter方法
- 通过Flutter调用原生方法,在原生方法里面实现调用Flutter方法。
以下是Flutter调用原生,并在原生里面调用flutter,此时flutter一定已经初始化并加载了。
完整代码请看原生Android调用Flutter的dart方法
2.3 EventChannel
待续…