如需转载,请注明出处:Flutter学习笔记(29)--Flutter如何与native进行通信

前言:在我们开发Flutter项目的时候,难免会遇到需要调用native api或者是其他的情况,这时候就需要处理Flutter与native的通信问题,一般常用的Flutter与native的通信方式有3中。

1.MethodChannel:Flutter端向native端发送通知,通常用来调用native的某一个方法。

2.EventChannel:用于数据流的通信,有监听功能,比如电量变化后直接推送给Flutter端。

3.BasicMessageChannel:用于传递字符串或半结构体的数据。

接下来具体看一下每种通信方式的使用方法!

  • MethodChannel

先来整体说一下逻辑思想吧,这样能更容易理解一些,如果想要实现Flutter与native通信,首先要建立一个通信的通道,通过一个通道标识来进行匹配,匹配上了之后Flutter端通过invokeMethod调用方法来发起一个请求,在native端通过onMethodCall进行匹配请求的key,匹配上了就处理对应case内的逻辑!!!整体来看,我感觉有点EventBus的意思呢,就像是一条事件总线。。。

第一步:实现通信插件Plugin-native端

由于一个项目中可能会需要很多Flutter与native的通信,所以我这里是将测试的插件封装到一个类里面了,然后在MainActivity里面的onCreate进行注册

  1. package com.example.flutter_demo;
  2.  
  3. import android.content.Context;
  4.  
  5. import io.flutter.plugin.common.MethodCall;
  6. import io.flutter.plugin.common.MethodChannel;
  7. import io.flutter.plugin.common.PluginRegistry;
  8.  
  9. public class TestPlugin implements MethodChannel.MethodCallHandler {
  10. public static String CHANNELNAME = "channel_name";//每一个通信通道的唯一标识,在整个项目内唯一!!!
  11. private static MethodChannel methodChannel;
  12. private Context context;
  13.  
  14. public TestPlugin(Context context) {
  15. this.context = context;
  16. }
  17.  
  18. public static void registerWith(PluginRegistry.Registrar registrar){
  19. methodChannel = new MethodChannel(registrar.messenger(),CHANNELNAME);
  20. TestPlugin instance = new TestPlugin(registrar.activity());
  21. methodChannel.setMethodCallHandler(instance);
  22. }
  23.  
  24. @Override
  25. public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
  26. if (methodCall.method.equals("method_key")){
  27. result.success("what is up man???");
  28. }
  29. }
  30. }

注:CHANNELNAME-->上面说过了,由于项目内会有很多的通信,所以我们定义的Channel必须是唯一的!!!!

TestPlugin实现MethodChannel.MethodCallHandler,定义一个对外暴露的注册方法registerWith,因为我们需要在MainActivity进行注册,在registerWith方法内初始化MethodChannel

接下来我们看一下onMethodCall方法,这个方法在Flutter发起请求时被调用,方法内有两个参数,一个methodCall和一个result,我们分别来说一下这两个参数:

methodCall:其中当前请求的相关信息,比如匹配请求的key

result:用于给Flutter返回数据,有3个方法,result.success(成功调用)、result.erro(失败调用)、result.notImplemented(方法没有实现调用)

第二步:注册通信插件Plugin-native端

  1. package com.example.flutter_demo;
  2.  
  3. import android.os.Bundle;
  4. import io.flutter.app.FlutterActivity;
  5. import io.flutter.plugins.GeneratedPluginRegistrant;
  6.  
  7. public class MainActivity extends FlutterActivity {
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. GeneratedPluginRegistrant.registerWith(this);
  12. TestPlugin.registerWith(this.registrarFor(TestPlugin.CHANNELNAME));
  13. }
  14. }

注册这块我感觉作用是起到了一个桥梁的作用,通过注册将插件和Flutter内定义的CHANNEL关联了起来。

第三步:Flutter内发起通信请求-flutter端

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3.  
  4. void main() => runApp(MyApp());
  5.  
  6. class MyApp extends StatefulWidget{
  7. @override
  8. State<StatefulWidget> createState() {
  9. // TODO: implement createState
  10. return new MyAppState();
  11. }
  12.  
  13. }
  14.  
  15. class MyAppState extends State<MyApp> {
  16. var _textContent = 'welcome to flutter word';
  17.  
  18. Future<Null> _changeTextContent() async{
  19. //channel_name每一个通信通道的唯一标识,在整个项目内唯一!!!
  20. const platfom = const MethodChannel('channel_name');
  21. try {
  22. //method_key是插件TestPlugin中onMethodCall回调匹配的key
  23. String resultValue = await platfom.invokeMethod('method_key');
  24. setState(() {
  25. _textContent = resultValue;
  26. });
  27. }on PlatformException catch (e){
  28. print(e.toString());
  29. }
  30. }
  31.  
  32. @override
  33. Widget build(BuildContext context) {
  34. // TODO: implement build
  35. return new MaterialApp(
  36. theme: new ThemeData(
  37. primaryColor: Colors.white,
  38. ),
  39. debugShowCheckedModeBanner: false,
  40. title: 'demo',
  41. home: new Scaffold(
  42. appBar: new AppBar(
  43. title: new Text('Demo'),
  44. leading: Icon(Icons.menu,size: ,),
  45. actions: <Widget>[
  46. Icon(Icons.search,size: ,)
  47. ],
  48. ),
  49. body: new Center(
  50. child: new Text(_textContent),
  51. ),
  52. floatingActionButton: new FloatingActionButton(onPressed: _changeTextContent,child: new Icon(Icons.adjust),),
  53. ),
  54. );
  55. }
  56. }

这里的功能就是页面中央有一个text,通过点击一个按钮,发起通信请求,通信成功在就收到native返回的数据后将text的文案修改。

我们看一下最终的效果:

                

MethodChannel通信是双向的,也就是说,Flutter端可以向native发起通信,native也可以向Flutter端发起通信,本质上就是反过来调用一下,原理上是同一个意思,具体的代码就不在这里写了,需要的话可以自行百度一下!

  • EventChannel

EventChannel的使用我们也以官方获取电池电量的demo为例,手机的电池状态是不停变化的。我们要把这样的电池状态变化由Native及时通过EventChannel来告诉Flutter。这种情况用之前讲的MethodChannel办法是不行的,这意味着Flutter需要用轮询的方式不停调用getBatteryLevel来获取当前电量,显然是不正确的做法。而用EventChannel的方式,则是将当前电池状态"推送"给Flutter。

第一步:MainActivity内注册EventChannel,并提供获取电量的方法-native端

  1. public class EventChannelPlugin implements EventChannel.StreamHandler {
  2.  
  3. private Handler handler;
  4. private static final String CHANNEL = "com.example.flutter_battery/stream";
  5. private int count = ;
  6.  
  7. public static void registerWith(PluginRegistry.Registrar registrar) {
  8. // 新建 EventChannel, CHANNEL常量的作用和 MethodChannel 一样的
  9. final EventChannel channel = new EventChannel(registrar.messenger(), CHANNEL);
  10. // 设置流的处理器(StreamHandler)
  11. channel.setStreamHandler(new EventChannelPlugin());
  12. }
  13.  
  14. @Override
  15. public void onListen(Object o, EventChannel.EventSink eventSink) {
  16. // 每隔一秒数字+1
  17. handler = new Handler(message -> {
  18. // 然后把数字发送给 Flutter
  19. eventSink.success(++count);
  20. handler.sendEmptyMessageDelayed(, );
  21. return false;
  22. });
  23. handler.sendEmptyMessage();
  24.  
  25. }
  26.  
  27. @Override
  28. public void onCancel(Object o) {
  29. handler.removeMessages();
  30. handler = null;
  31. count = ;
  32. }
  33. }

其中onCancel代表对面不再接收,这里我们应该做一些clean up的事情。而 onListen则代表通道已经建好,Native可以发送数据了。注意onListen里带的EventSink这个参数,后续Native发送数据都是经过EventSink的。

第二步:同MethodChannel一样,发起通信请求

  1. class _MyHomePageState extends State<MyHomePage> {
  2. // 创建 EventChannel
  3. static const stream = const EventChannel('com.example.flutter_battery/stream');
  4.  
  5. int _count = ;
  6.  
  7. StreamSubscription _timerSubscription;
  8.  
  9. void _startTimer() {
  10. if (_timerSubscription == null)
  11. // 监听 EventChannel 流, 会触发 Native onListen回调
  12. _timerSubscription = stream.receiveBroadcastStream().listen(_updateTimer);
  13. }
  14.  
  15. void _stopTimer() {
  16. _timerSubscription?.cancel();
  17. _timerSubscription = null;
  18. setState(() => _count = );
  19. }
  20.  
  21. void _updateTimer(dynamic count) {
  22. print("--------$count");
  23. setState(() => _count = count);
  24. }
  25.  
  26. @override
  27. void dispose() {
  28. super.dispose();
  29. _timerSubscription?.cancel();
  30. _timerSubscription = null;
  31. }
  32.  
  33. @override
  34. Widget build(BuildContext context) {
  35. return Scaffold(
  36. appBar: AppBar(
  37. title: Text(widget.title),
  38. ),
  39. body: Container(
  40. margin: EdgeInsets.only(left: , top: ),
  41. child: Center(
  42. child: Column(
  43. children: [
  44. Row(
  45. children: <Widget>[
  46. RaisedButton(
  47. child: Text('Start EventChannel',
  48. style: TextStyle(fontSize: )),
  49. onPressed: _startTimer,
  50. ),
  51. Padding(
  52. padding: EdgeInsets.only(left: ),
  53. child: RaisedButton(
  54. child: Text('Cancel EventChannel',
  55. style: TextStyle(fontSize: )),
  56. onPressed: _stopTimer,
  57. )),
  58. Padding(
  59. padding: EdgeInsets.only(left: ),
  60. child: Text("$_count"),
  61. )
  62. ],
  63. )
  64. ],
  65. ),
  66. ),
  67. ),
  68. );
  69. }
  70. }

整体说明一下:Flutter端通过stream.receiveBroadcastStream().listen监听native发送过来的数据,native端通过eventSink.success(++count)不断的将数据返回给Flutter端,这样就实现了我们想要的实时监听的效果了!

  • BasicMessageChannel

其实他就是一个简版的MethodChannel,也可以说MethodChannel是基于BasicMessageChannel实现的,BasicMessageChannel只是进行通信,更通俗的理解就是两端发通知,但是不需要进行方法匹配。

第一步:初始化及注册-native

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4.  
  5. // 省略其他代码...
  6.  
  7. messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE);
  8. messageChannel.
  9. setMessageHandler(new MessageHandler<String>() {
  10. @Override
  11. public void onMessage(String s, Reply<String> reply) {
  12. // 接收到Flutter消息, 更新Native
  13. onFlutterIncrement();
  14. reply.reply(EMPTY_MESSAGE);
  15. }
  16. });
  17.  
  18. FloatingActionButton fab = findViewById(R.id.button);
  19. fab.setOnClickListener(new View.OnClickListener() {
  20. @Override
  21. public void onClick(View v) {
  22. // 通知 Flutter 更新
  23. sendAndroidIncrement();
  24. }
  25. });
  26. }
  27.  
  28. private void sendAndroidIncrement() {
  29. messageChannel.send(PING);
  30. }
  31.  
  32. private void onFlutterIncrement() {
  33. counter++;
  34. TextView textView = findViewById(R.id.button_tap);
  35. String value = "Flutter button tapped " + counter + (counter == ? " time" : " times");
  36. textView.setText(value);
  37. }

第二步:Flutter端发起通信-flutter

  1. class _MyHomePageState extends State<MyHomePage> {
  2. static const String _channel = 'increment';
  3. static const String _pong = 'pong';
  4. static const String _emptyMessage = '';
  5. static const BasicMessageChannel<String> platform =
  6. BasicMessageChannel<String>(_channel, StringCodec());
  7.  
  8. int _counter = ;
  9.  
  10. @override
  11. void initState() {
  12. super.initState();
  13. // 设置消息处理器
  14. platform.setMessageHandler(_handlePlatformIncrement);
  15. }
  16.  
  17. // 如果接收到 Native 的消息 则数字+1
  18. Future<String> _handlePlatformIncrement(String message) async {
  19. setState(() {
  20. _counter++;
  21. });
  22. // 发送一个空消息
  23. return _emptyMessage;
  24. }
  25.  
  26. // 点击 Flutter 中的 FAB 则发消息给 Native
  27. void _sendFlutterIncrement() {
  28. platform.send(_pong);
  29. }
  30.  
  31. @override
  32. Widget build(BuildContext context) {
  33. return Scaffold(
  34. appBar: AppBar(
  35. title: Text('BasicMessageChannel'),
  36. ),
  37. body: Container(
  38. child: Column(
  39. crossAxisAlignment: CrossAxisAlignment.start,
  40. children: <Widget>[
  41. Expanded(
  42. child: Center(
  43. child: Text(
  44. 'Platform button tapped $_counter time${_counter == 1 ? '' : 's'}.',
  45. style: const TextStyle(fontSize: 17.0)),
  46. ),
  47. ),
  48. Container(
  49. padding: const EdgeInsets.only(bottom: 15.0, left: 5.0),
  50. child: Row(
  51. children: <Widget>[
  52. Image.asset('assets/flutter-mark-square-64.png', scale: 1.5),
  53. const Text('Flutter', style: TextStyle(fontSize: 30.0)),
  54. ],
  55. ),
  56. ),
  57. ],
  58. )),
  59. floatingActionButton: FloatingActionButton(
  60. onPressed: _sendFlutterIncrement,
  61. child: const Icon(Icons.add),
  62. ),
  63. );
  64. }
  65. }

总结:以上就是Flutter和native通信的全部内容了,理解了以后其实很简单,上面的内容有一些我个人的理解,更深一层的还需要继续挖掘!

Flutter学习笔记(29)--Flutter如何与native进行通信的更多相关文章

  1. Flutter学习笔记(30)--Android原生与Flutter混编

    如需转载,请注明出处:Flutter学习笔记(30)--Android原生与Flutter混编 这篇文章旨在学习如何在现有的Android原生项目上集成Flutter,实现Android与Flutte ...

  2. Flutter学习笔记(3)--Dart变量与基本数据类型

    一.变量 在Dart里面,变量的声明使用var.Object或Dynamic关键字,如下所示: var name = ‘张三’: 在Dart语言里一切皆为对象,所以如果没有将变量初始化,那么它的默认值 ...

  3. Flutter学习笔记(4)--Dart函数

    如需转载,请注明出处:Flutter学习笔记(4)--Dart函数 Dart是一个面向对象的语言,所以函数也是对象,函数属于Function对象,函数可以像参数一样传递给其他函数,这样便于做回调处理: ...

  4. Flutter学习笔记(5)--Dart运算符

    如需转载,请注明出处:Flutter学习笔记(5)--Dart运算符 先给出一个Dart运算符表,接下来在逐个解释和使用.如下:                            描述       ...

  5. Flutter学习笔记(6)--Dart异常处理

    如需转载,请注明出处:Flutter学习笔记(6)--Dart异常处理 异常是表示发生了意外的错误,如果没有捕获异常,引发异常的隔离程序将被挂起,并且程序将被终止: Dart代码可以抛出并捕获异常,但 ...

  6. Flutter学习笔记(8)--Dart面向对象

    如需转载,请注明出处:Flutter学习笔记(7)--Dart异常处理 Dart作为高级语言,支持面向对象的很多特性,并且支持基于mixin的继承方式,基于mixin的继承方式是指:一个类可以继承自多 ...

  7. Flutter学习笔记(9)--组件Widget

    如需转载,请注明出处:Flutter学习笔记(9)--组件Widget 在Flutter中,所有的显示都是Widget,Widget是一切的基础,我们可以通过修改数据,再用setState设置数据(调 ...

  8. Flutter学习笔记(10)--容器组件、图片组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 上一篇Flutter学习笔记(9)--组件Widget我们说到了在Flutter中一个非常重要的理念"一切皆为组件 ...

  9. Flutter学习笔记(11)--文本组件、图标及按钮组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 文本组件 文本组件(text)负责显示文本和定义显示样式,下表为text常见属性 Text组件属性及描述 属性名 类型 默认 ...

随机推荐

  1. Count on a tree 树上区间第K小

    Count on a tree 题意:求路径 u到v上的 第k小的权重. 题解:先DFS建数, 然后对于每个节点往上跑出一颗主席树, 然后每次更新. 查询的时候, u, v, k, 找到  z = l ...

  2. 深入浅出TypeScript(5)- 在React项目中使用TypeScript

    前言 在第二小节中,我们讨论了利用TypeScript创建Web项目的实现,在本下节,我们讨论一下如何结合React创建一个具备TypeScript类型的应用项目. 准备 Webpack配置在第二小节 ...

  3. js中数组方法大全

    js数组方法大全 一:前言 我们在学到js中数组的时候,我们会接触到js中数组的一些方法,这些方法对我们来说,可以很遍历的达到我们想要的结果,但是因为方法比较多,有些方法也不常用,可能会过一段时间就会 ...

  4. MySQL基础/数据库和表的设计

    MySQL基础 一:安装MySQL(按步骤操作,如果下载后使用不了,试着用360安全卫士卸载MySQL,清除残留的,方便在下载造成不必要的麻烦:如果这样也不行,那就需要重做系统在进行下载) 二:创建数 ...

  5. Jsp学习笔记(4)——分页查询

    核心sql i是第几页,itemNum是每页显示的数据条数 select * from ( select e.*,rownum rn from ( select * from employee whe ...

  6. 【Rocketmq】通过 docker 快速搭建 rocketmq 环境

    1. 安装 Namesrv 拉取镜像 docker pull rocketmqinc/rocketmq:4.4.0` 启动容器 docker run -d -p 9876:9876 -v {RmHom ...

  7. Hbase 日常运维

    日常维护的命令 1,major_compact 'testtable',通常生产环境会关闭自动major_compact(配置文件中hbase.hregion.majorcompaction设 为0) ...

  8. spring scope prototype与singleton区别

    1.singleton作用域  当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配 ...

  9. http响应

    1.http响应 2.响应行常见状态码 200 :请求成功. 302 :请求重定向. 当访问网址A时,由于网址A服务器端的拦截器或者其他后端代码处理的原因,会被重定向到网址B. 304 :请求资源没有 ...

  10. 深入理解JVM内存分配策略

    理解JVM内存分配策略 三大原则+担保机制 JVM分配内存机制有三大原则和担保机制 具体如下所示: 优先分配到eden区 大对象,直接进入到老年代 长期存活的对象分配到老年代 空间分配担保 对象优先在 ...