前言
随着Flutter混合开发的项目越来越多,我们也有了实际的一个场景,
那就是Flutter如何与原生(Native)端进行通信
目前看来,大概有三种方式,分别是:
MethodChannel
EventChannel
MessageChannel
这三种方式实现起来其实大同小异,基本的代码都差不多
但为了让大家更清楚的知道三者的区别,所以有了这篇博客
本篇主要讲MethodChannel方式
这也是实际开发中最常用的方式。
其他的两种方式可以看下一篇博客:
EventChannel和BasicMessageChannel
那么现在正式开始
实现
FlutterActivity
首先我们要知道,所谓的Flutter的一个个page页面,其实归根结底也是跑在Activity上面的
这有点像我们的WebView,最终承载页面的,还是一个Activity
而在Flutter中,承载页面的,其实是一个叫做FlutterActivity的类
于是我们的交互,其实是Flutter的Page和这个FlutterActivity进行交互
首先我们新建一个普通的Activity,也就是首页
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewById<TextView>(R.id.textview).setOnClickListener {startActivity(MyFlutterActivity.withCachedEngine("your_engine_id").build(this))}}}
这个首页就是我们最基础的AppCompatActivity
它不是重点。
点击按钮进入我们承载Flutter的页面:MyFlutterActivity
它继承的是一个叫做FlutterFragmentActivity的类
这个页面就可以正式和他自己页面里的Flutter页面交互了
相关交互的代码也是写在这里
Flutter端调用Android端
先看Flutter端我们如何实现
首先我们可以新建一个ForNativePlugin.dart的文件
这个文件用来定义要调用Android端的方法
class ForNativePlugin {MethodChannel methodChannel = const MethodChannel("ForNativePlugin");/// 调用安卓,得到当前时间Future<String> getTime() async {var nowTime = await methodChannel.invokeMethod("getTime");return nowTime;}/// 显示安卓土司showAndroidToast(String msg) async {try {await methodChannel.invokeMethod('toast', {'msg': msg});} on PlatformException catch (e) {print(e.toString());}}
}
可以看到,创建了一个MethodChannel类
这个就是交互的关键类
MethodChannel的name属性要双端开发约定好
这里约定的是一个ForNativePlugin的字符串
然后定义了两个方法
方法的实现其实就是用methodChanneld.invokeMethod去触发安卓端的方法
invokeMethod的一个参数写安卓端的方法名,第二个写入参的名字,入参可以有多个
写好后,我们Flutter就可以创建这个类
ForNativePlugin forNativePlugin = ForNativePlugin();
然后在需要的时候使用我们写的这个工具类去触发Android端的方法了
比如:
forNativePlugin.showAndroidToast("当前时间为:${forNativePlugin.getTime()}");
这样就可以调用安卓端,获取时间,然后再让安卓端帮我们把时间返回后
通过Toast的方法弹出来
那么Android端要怎么写呢?
也很简单,Android端也要定义一个类
实现MethodCallHandler接口的onMethodCall方法
class MyMethodCallHandler(var context: Context) : MethodCallHandler {override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {when (methodCall.method) {"getTime" -> {result.success(SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(System.currentTimeMillis()))}"toast" -> {if (methodCall.hasArgument("msg") && !TextUtils.isEmpty(methodCall.argument<Any>("msg").toString())) {Toast.makeText(context, methodCall.argument<Any>("msg").toString(), Toast.LENGTH_LONG).show()} else {
// result.error()Toast.makeText(context, "msg 不能为空", Toast.LENGTH_SHORT).show()}}else -> {// 表明没有对应实现result.notImplemented()}}}
}
onMethodCall方法就用来处理我们要被触发的逻辑了
然后来到我们继承了FlutterFragmentActivity的类下面
首先也定义一个MethodChannel
private lateinit var flutterMethodChannel: MethodChannel
然后让我们Activity去实现configureFlutterEngine方法
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)//通过MethodChannel与原生通信,ForNativePlugin为约定字段flutterMethodChannel = MethodChannel(flutterEngine.dartExecutor, "ForNativePlugin")flutterMethodChannel.setMethodCallHandler(MyMethodCallHandler(this))}
可以看到,这里其实就是一个注册环节
把我们的MyMethodCallHandler类注册到MethodChannel里面
至此,Flutter端调用Android端代码就完成了
补充一点:
在Android端被触发时
可以通过result类告知Flutter请求结果是否成功
result.success() 成功
result.error() 失败
result.notImplemented() 该方法没有对应实现
Android端调用Flutter端
Android端调用Flutter端其实也差不多
首先是Android端:
我们可以通过调用getFlutterEngine方法获得一个FlutterEngine
然后拿这个FlutterEngine的getDartExecutor进行操作
fun toFlutter(num1: Int, num2: Int) {flutterEngine?.dartExecutor?.let {nativeMethodChannel = MethodChannel(it, "ForFlutterPlugin")nativeMethodChannel.invokeMethod("callFlutterSum",listOf<Int>(num1, num2),object : MethodChannel.Result {override fun success(result: Any?) {Log.d("MyFlutterActivity", "计算结果为 = $result")}override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {Log.d("MyFlutterActivity", "出错了:$errorCode, $errorMessage, $errorDetails")}override fun notImplemented() {Log.d("MyFlutterActivity", "notImplemented")}})}}
这里可以看到,也是定义了一个MethodChannel,约定字段为:ForFlutterPlugin
然后调用Flutter端的callFlutterSum方法获取计算的返回值
这里需要注意一点,调用Fluter代码要在主线程
然后来到Flutter端
定义好Flutter端的MethodChannel:
MethodChannel methodChannel = const MethodChannel("ForFlutterPlugin");
并执行注册和回调监听,可以在initState方法中注册:
void initState() {// TODO: implement initStatesuper.initState();methodChannel.setMethodCallHandler((call) {switch (call.method) {case 'callFlutterSum':print('call callMe : arguments = ${call.arguments}');List<Object?> numbers = call.arguments;var num1=numbers[0] as int;var num2=numbers[1] as int;//触发Android端的success方法return Future.value(num1+num2);//触发Android端的error方法,errorCode为error,errorMessage为:调用出错,不能传details// return Future.error("调用出错");//触发Android端的error方法,可以额外传details对象throw PlatformException(code: 'errorCode', message: 'Error occurred',details: "出错了");default:print('Unknowm method ${call.method}');//触发Android端的notImplemented方法throw MissingPluginException();}},);}
至此,Flutter端的工作也已完成
可以看到,两端的互相调用,基本分为三个步骤:
1:被调用端先写好被调用的处理逻辑
2:被调用端创建好MethodChannel,调用setMethodCallHandler进行注册
3:调用端创建MethodChannel,约定好字段,然后使用invokeMethod主动去调用方法
注意
在你写双端通信的时候,如果发现不触发调用,可以关注以下几个原因:
1:是否是继承了FlutterFragmentActivity或者FlutterActivity
2:约定字段是否一致
3:方法名称和参数是否一致
源码
本篇博客源码地址:
MethodChannel