Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)

前言
上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信。
并且也看到了 Flutter 内部 EventChannel 源码也是对 MethodChannel 的封装。
因此这篇我们来说下如何通过 MethodChannel 实现 Android -> Flutter 的通信。
至于 Flutter -> Android 的通信,没看过的小伙伴建议看下之前的文章 Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信。
既然我们之前写过 Flutter -> Android 的 MethodChannel,那么我们现在要写 Android -> Flutter 的 MethodChannel,可以仿照一下原先的写法。
步骤如下:
第一步:Flutter UI 修改
我们的代码在上一篇的基础上做修改,在列上面增加一个文本用于确认收到了 Android 的请求。
String _arguments = 'unknown';
Text(_arguments),
第二步:在 Android 端写 invokeMethod 引用 Flutter 方法
methodChannel.invokeMethod("getContent", "arguments", new MethodChannel.Result() {
    @Override
    public void success(@Nullable Object o) {
        Log.e(TAG, "success="+o);
    }
    @Override
    public void error(String s, @Nullable String s1, @Nullable Object o) {
        Log.e(TAG, "error="+s);
    }
    @Override
    public void notImplemented() {
        Log.e(TAG, "notImplemented");
    }
});
参数说明:
第一个为方法名。用于 Flutter 区分 Android 的不同请求。
第二个为参数值。用于 Android 需要给 Flutter 传递的额外数据。
第三个为 Android -> Flutter 请求的结果回调。
回调有三种情况:
1)调用成功
2)调用失败
3)Flutter 未实现对应方法
第三步:在 Flutter 调用对应 MethodChannel 的 setMethodCallHandler
methodChannel.setMethodCallHandler((MethodCall call){
    if (call?.method == 'getContent') {
      setState(() {
        _arguments = call?.arguments ?? '';
      });
    }
});
看到这里的 MethodCall 你应该很熟悉了,通过 call.method 可以知道 Android 要获取的方法名,通过 call.arguments 可以拿到 Android 传递过来的数据。
这里的 getContent 对应 Android 的 invokeMethod。
为了确认我们获取到了,我们将 Android 传递过来的参数显示出来。
第四步:运行
可以看到效果如下:
初始显示 unknown

点击后显示原生传过来的内容

同时控制台显示打印信息如下:
success=null
我们发现 Android 确实回调成功了,但是另一个问题随之而来,Flutter 如何将内容回调给 Android?
解决问题一时爽,一直解决问题一直爽。
也是很简单的,就是我们写一个异步方法将信息带回去即可。
在 setState 方法后面添加下面代码:
return returnToRaw();
具体方法实现如下:
Future<String> returnToRaw() async {
  return 'received your message';
}
这个时候再运行点击按钮会发现控制台打印如下信息:
success=received your message
可以看到成功收到返回值了。
这里演示返回的是字符串,因此异步方法返回类型是 Future 。如果你要返回其他类型,可以自行修改。
如果希望回调 notImplemented,不要在 Flutter 调用 MethodChannel 的 setMethodCallHandler 或者 setMethodCallHandler 的参数设置为 null 即可。
    //方法一
//    methodChannel.setMethodCallHandler((MethodCall call){
//        if (call?.method == 'getContent') {
//          setState(() {
//            _arguments = call?.arguments ?? '';
//          });
//          return returnToRaw();
//        }
//    });
    //方法二
    methodChannel.setMethodCallHandler(null);
如果希望回调 error,修改 returnToRaw 方法即可。如下:
Future<String> returnToRaw() async {
  throw PlatformException(code: 'error code');
}
这里通过抛出 PlatformException 并将错误信息带回去给 Android。
一般错误信息除非是手动需要抛,否则源码会帮我们处理的。
这里是为了演示所以手动抛出异常。
好了,至此 MethodChannel Android-> Flutter 我们也实现了。
其实不管是 Android -> Flutter 还是 Flutter-> Android,都是平台相关代码。
因此可以直接到 platform_channel.dart 里面看看源码。
除了 EventChannel、MethodChannel,还有 BasicMessageChannel 和 OptionalMethodChannel,这些就交给小伙伴们自己去研究了。
后记
这边分享一下研究 MethodChannel 实现 Android -> Flutter 的过程遇到的坑。
希望不止是授大家以鱼,更是授大家以渔。
坑1:一开始将原生 MethodChannel 写到外面,导致 Flutter 没收到请求
因为 Flutter 是在 initState 里面去 setMethodCallHandler 的,而 debug 模式下可能 Flutter 还没加载完成,这个时候发送消息,Flutter 就可能没收到。
后面改成点击之后 Flutter -> Android,Android 再发给 Flutter。
这个问题是异步的原因导致的。
明确之后通过正确的方式就可以收到请求了。
坑2:Flutter 收到之后,如何回调回消息呢?
首先点击进入 setMethodCallHandler 源码,如下:
void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
  BinaryMessages.setMessageHandler(
    name,
    handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
  );
}
再深入 _handleAsMethodCall,如下:
Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
  final MethodCall call = codec.decodeMethodCall(message);
  try {
    return codec.encodeSuccessEnvelope(await handler(call));
  } on PlatformException catch (e) {
    return codec.encodeErrorEnvelope(
      code: e.code,
      message: e.message,
      details: e.details,
    );
  } on MissingPluginException {
    return null;
  } catch (e) {
    return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
  }
}
到这里就比较明朗了。
可以看到错误基本不用我们处理,也没有太多可介入空间。
但是成功回调,这里核心语句是
await handler(call)
因此我们上面通过一个异步方法返回字符串给原生。
由于笔者之前对 Future 不是很熟,因此为了解决这个问题,看了 dart 源码
Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)的更多相关文章
- Flutter 即学即用系列博客——09 EventChannel 实现原生与 Flutter 通信(一)
		
前言 紧接着上一篇,这一篇我们讲一下原生怎么给 Flutter 发信号,即原生-> Flutter 还是通过 Flutter 官网的 Example 来讲解. 案例 接着上一次,这一次我们让原生 ...
 - Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信
		
背景 前面我们讲了很多 Flutter 相关的知识点,但是我们并没有介绍怎样实现 Flutter 与原生的通信. 比如我在 Flutter UI 上面点击了一个按钮,我希望原生做一些处理,那么原生怎么 ...
 - Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
		
前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...
 - Flutter 即学即用系列博客总结篇
		
前言 迟到的总结篇,其实大家看我之前发的系列博客最后一篇,发文时间是 3 月 29 日.距离现在快两个月了. 主要是因为有很多事情在忙,所以这篇就耽搁了. 今天终于可以跟大家会面了. 系列博客背景 F ...
 - Flutter 即学即用系列博客——05 StatelessWidget vs StatefulWidget
		
前言 上一篇我们对 Flutter UI 有了一个基本的了解. 这一篇我们通过自定义 Widget 来了解下如何写一个 Widget? 然而 Widget 有两个,StatelessWidget 和 ...
 - Flutter 即学即用系列博客——04 Flutter UI 初窥
		
前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...
 - Flutter 即学即用系列博客——06 超实用 Widget 集锦
		
本篇文章我们来讲讲一些比较常用的 Widget. 大家验证的时候使用下面的代码替换 main.dart 代码,然后在 //TODO 语句返回下面常用 Widget 示例的代码. import 'pac ...
 - Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明
		
前言 上一篇文章我们搭建好了 Flutter 的开发环境. Flutter 即学即用--01 环境搭建 这一篇我们通过 Flutter 的一个 Demo 来了解下 Flutter. 开发系统:MAC ...
 - Flutter 即学即用系列博客——10 混淆
		
前言 之前的博客我们都是在 debug 的模式下进行开发的. 实际发布到市场或者给到用户的都是 release 包. 而对于 Android 来说,release 包一个重要的步骤就是混淆. Andr ...
 
随机推荐
- Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载)
			
Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载) 说明:Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构,我采用以下三种维度来讲解 1. 代码层面. 2. 数 ...
 - "unresolved reference 'appium' "问题解决
			
根据github的教程安装好"Appium-Python-Client"后,代码里写入"from appium import webdriver"就报错&quo ...
 - 【爆料】-《昆士兰大学毕业证书》Queensland一模一样原件
			
☞昆士兰大学毕业证书[微/Q:2544033233◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归& ...
 - PCB泪滴设计
			
操作:选择[Tools]-->[Teardrops],快捷键T+E.打开[Teardrop Options]对话框进行设置.如下图所示: 对话框面板介绍 [General] 1.该区域的[Pad ...
 - java基于BasicPlayer调用 播放音乐
			
无聊中想想用java调用下听音乐的api.晚上很多文章用的比较老大方法了,都是用原生的代码写,而且不支持mp3格式,BasicPlayer第三方包提供了很好的api调用,简单的3行代码就可以调用mp3 ...
 - Postman-----将 A 请求中 response Body 中的参数值传入到下一个请求 B 的 request body 中作为参数发送请求
			
问题:将A接口中response body的"id"传入到B接口的request body中. 解决办法: 1.在A接口的test中设置环境变量. 代码:var data = JS ...
 - #define指令
			
#define指令: 使用#define的标准格式: #define PI 3.14159 注意:结尾不加分号(;),也不要写成PI = 3.14159,我们最好用大写表示符号常量/明示常量(PI). ...
 - 干货,一文带你超详细了解Session的原理及应用
			
session 简介 session 是我们 jsp 九大隐含对象的一个对象. session 称作域对象,他的作用是保存一些信息,而 session 这个域对象是一次会话期间使用同一个对象.所以这个 ...
 - 带你学习AOP框架之Aspect.Core[1]
			
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...
 - [翻译 EF Core in Action 2.0] 查询数据库
			
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...