flutter
可以与native之间进行通信,帮助我们使用native提供的能力。通信是双向的,我们可以从Native层调用flutter层的dart代码,同时也可以从flutter层调用Native的代码。我们需要使用Platform Channels APIs进行通信,主要包括下面三种:
- [MethodChanel]:用于传递方法调用(method invocation)
- [EventChannel]:用于事件流的发送(event streams)
- [MessageChannel]:用于传递字符串和半结构化的消息
其中最常用的是MethodChanel,MethodChanel的使用与在Android的JNI调用非常类似,但是MethodChanel更加简单,而且相对于JNI的同步调用MethodChanel的调用是异步的:
1. MethodChanel的基本流程
从flutter架构图上可以看到,flutter与native的通信发生在Framework和Engine之间,framewrok内部会将MethodChannel以BinaryMessage的形式与Engine进行数据交换。关于BinaryMessage在这里不做过多介绍,主要以介绍Channel的使用为主。
我们先看一下MethodChanel使用的基本流程:
flutter调用native
- [native] 使用MethodChannel#setMethodCallHandler注册回调
- [flutter] 通过MethodChannel#invokeMethod发起异步调用
- [native] 调用native方法通过Result#success 返回Result,出错时返回error
- [flutter] 收到native返回的Result
native调用flutter
与flutter调用native的顺序完全一致,只是[native]与[flutter]角色反调
2. 代码实现
flutter调用native
首先在flutter端实现以下功能:
- 创建MethodChannel,并注册channel名,一般使用“包名/标识”作为channel名
- 通过invokeMethod发起异步调用,invokeMethod接受两个参数:
- method:调用的native方法名
- arguments:nativie方法参数,有多个参数时需要以map形式指定
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const MethodChannel _channel = const MethodChannel('com.example.methodchannel/interop');
static Future<dynamic> get _list async {
final Map params = <String, dynamic> {
'name': 'my name is hoge',
'age': 25,
};
final List<dynamic> list = await _channel.invokeMethod('getList', params);
return list;
}
@override
initState() {
super.initState();
// Dart -> Platforms
_list.then((value) => print(value));
}
在native(android)端实现以下功能
- 创建MethodChannel,必须跟flutter中使用相同的注册字符串
- 设置MethodCallHander,methodCall中传递来自flutter的参数
- 通过result返回给flutter结果
class MainActivity: FlutterActivity() {
companion object {
private const val CHANNEL = "com.example.methodchannel/interop"
private const val METHOD_GET_LIST = "getList"
}
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
if (methodCall.method == METHOD_GET_LIST) {
val name = methodCall.argument<String>("name").toString()
val age = methodCall.argument<Int>("age")
Log.d("Android", "name = ${name}, age = $age")
val list = listOf("data0", "data1", "data2")
result.success(list)
}
else
result.notImplemented()
}
}
因为结果返回是异步的,所以既可以像上面代码那样在MethodCallHandler里通过result.success返回结果,也也可以先保存result的引用,在之后的某个时间点再调用sucess,但需要特别注意的是无论何时调用result.sucess,必须确保其在UI线程进行:
@UiThread void success(@Nullable Object result)
native调用flutter
android调用flutter的代码实现与flutter调用android是类似的,只不过要注意所以的调用都要在UI线程进行。
先实现android部分的代码:
channel.invokeMethod("callMe", listOf("a", "b"), object : MethodChannel.Result {
override fun success(result: Any?) {
Log.d("Android", "result = $result")
}
override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
Log.d("Android", "$errorCode, $errorMessage, $errorDetails")
}
override fun notImplemented() {
Log.d("Android", "notImplemented")
}
})
result.success(null)
flutte部分则主要实现MethodCallHandler的注册:
Future<dynamic> _platformCallHandler(MethodCall call) async {
switch (call.method) {
case 'callMe':
print('call callMe : arguments = ${call.arguments}');
return Future.value('called from platform!');
//return Future.error('error message!!');
default:
print('Unknowm method ${call.method}');
throw MissingPluginException();
break;
}
}
@override
initState() {
super.initState();
// Platforms -> Dart
_channel.setMethodCallHandler(_platformCallHandler);
}