首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 混合开发(Android)Flutter跟Native相互通信

Flutter 混合开发(Android)Flutter跟Native相互通信

作者头像
conanma
发布2022-01-10 12:02:52
6870
发布2022-01-10 12:02:52
举报
文章被收录于专栏:正则正则

前言

Flutter 作为混合开发,跟native端做一些交互在所难免,比如说调用原生系统传感器、原生端的网络框架进行数据请求就会用到 Flutter 调用android 及android 原生调用 Flutter的方法,这里就涉及到Platform Channels(平台通道)

Platform Channels (平台通道)

Flutter 通过Channel 与客户端之间传递消息,如图:

图中就是通过MethodChannel的方式实现Flutter 与客户端之间的消息传递。MethodChannel是Platform Channels中的一种,Flutter有三种通信类型:

BasicMessageChannel:用于传递字符串和半结构化的信息

MethodChannel:用于传递方法调用(method invocation)通常用来调用native中某个方法

EventChannel: 用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端。

为了保证UI的响应,通过Platform Channels传递的消息都是异步的。

更多关于channel原理可以去看这篇文章:channel原理篇

Platform Channels 使用

1.MethodChannel的使用

原生客户端写法(以Android 为例)

首先定义一个获取手机电量方法

private int getBatteryLevel() {
        return 90;
    }

这函数是要给Flutter 调用的方法,此时就需要通过 MethodChannel 来建立这个通道了。

首先新增一个初始化 MethodChannel 的方法

private String METHOD_CHANNEL = "common.flutter/battery";
private String GET_BATTERY_LEVEL = "getBatteryLevel";
private MethodChannel methodChannel;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        initMethodChannel();
        getFlutterView().postDelayed(() ->
            methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
                @Override
                public void success(@Nullable Object o) {
                    Log.d(TAG, "get_message:" + o.toString());
                }

                @Override
                public void error(String s, @Nullable String s1, @Nullable Object o) {

                }

                @Override
                public void notImplemented() {

                }
            }), 5000);

    }

    private void initMethodChannel() {
        methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
        methodChannel.setMethodCallHandler(
                (methodCall, result) -> {
                    if (methodCall.method.equals(GET_BATTERY_LEVEL)) {
                        int batteryLevel = getBatteryLevel();

                        if (batteryLevel != -1) {
                            result.success(batteryLevel);
                        } else {
                            result.error("UNAVAILABLE", "Battery level not available.", null);
                        }
                    } else {
                        result.notImplemented();
                    }
                });


    }

    private int getBatteryLevel() {
        return 90;
    }

METHOD_CHANNEL 用于和flutter交互的标识,由于一般情况下会有多个channel,在app里面需要保持唯一性

MethodChannel 都是保存在以通道名为Key的Map中。所以要是设了两个名字一样的channel,只有后设置的那个会生效。

onMethodCall 有两个参数,onMethodCall 里包含要调用的方法名称和参数。Result是给Flutter的返回值。方法名是客户端与Flutter统一设定。通过if/switch语句判断 MethodCall.method 来区分不同的方法,在我们的例子里面我们只会处理名为“getBatteryLevel”的调用。在调用本地方法获取到电量以后通过 result.success(batteryLevel) 调用把电量值返回给Flutter。

MethodChannel-Flutter 端

直接先看一下Flutter端的代码

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  static const platform = const MethodChannel('common.flutter/battery');

  void _incrementCounter() {
    setState(() {
      _counter++;
      _getBatteryLevel();
    });
  }

  @override
  Widget build(BuildContext context) {
    platform.setMethodCallHandler(platformCallHandler);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            Text('$_batteryLevel'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }

  String _batteryLevel = 'Unknown battery level.';

  Future<Null> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

 //客户端调用
  Future<dynamic> platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case "get_message":
        return "Hello from Flutter";
        break;
    }
  }
}

上面代码解析: 首先,定义一个常量result.success(platform),和Android客户端定义的channel一致; 接下来定义一个 result.success(_getBatteryLevel())方法,用来调用Android 端的方法,result.success(final int result = await platform.invokeMethod('getBatteryLevel');) 这行代码就是通过通道来调用Native(Android)方法了。因为MethodChannel是异步调用的,所以这里必须要使用await关键字。

在上面Android代码中我们把获取到的电量通过result.success(batteryLevel);返回给Flutter。这里await表达式执行完成以后电量就直接赋值给result变量了。然后通过result.success(setState); 去改变Text显示值。到这里为止,是通过Flutter端调用原生客户端方法。

MethodChannel 其实是一个可以双向调用的方法,在上面的代码中,其实我们也体现了,通过原生客户端调用Flutter的方法。

在原生端通过 methodChannel.invokeMethod 的方法调用

methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
                @Override
                public void success(@Nullable Object o) {
                    Log.d(TAG, "get_message:" + o.toString());
                }

                @Override
                public void error(String s, @Nullable String s1, @Nullable Object o) {

                }

                @Override
                public void notImplemented() {

                }
            });

在Flutter端就需要给MethodChannel设置一个MethodCallHandler

static const platform = const MethodChannel('common.flutter/battery');
platform.setMethodCallHandler(platformCallHandler);
Future<dynamic> platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case "get_message":
        return "Hello from Flutter";
        break;
    }
  }

以上就是MethodChannel的相关用法了。

EventChannel

将数据推送给Flutter端,类似我们常用的推送功能,有需要就推送给Flutter端,是否需要去处理这个推送由Flutter那边决定。相对于MethodChannel是主动获取,EventChannel则是被动推送。

EventChannel 原生客户端写法

private String EVENT_CHANNEL = "common.flutter/message";
private int count = 0;
private Timer timer;

private void initEventChannel() {
        new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        if (count < 10) {
                            count++;
                            events.success("当前时间:" + System.currentTimeMillis());
                        } else {
                            timer.cancel();
                        }
                    }
                }, 1000, 1000);
            }

            @Override
            public void onCancel(Object o) {

            }
        });
    }

在上面的代码中,我们做了一个定时器,每秒向Flutter推送一个消息,告诉Flutter我们当前时间。为了防止一直倒计时,我这边做了个计数,超过10次就停止发送。

EventChannel Flutter端

String message = "not message";
static const eventChannel = const EventChannel('common.flutter/message');
@override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

void _onEvent(Object event) {
    setState(() {
      message =
      "message: $event";
    });
  }

  void _onError(Object error) {
    setState(() {
      message = 'message: unknown.';
    });
  }

上面的代码就是Flutter端接收原生客户端数据,通过_onEvent 来接收数据,将数据显示Text。这个实现相对简单,如果要达到业务分类,需要将数据封装成json,通过json数据包装一些对应业务标识和数据来做区分。

BasicMessageChannel

BasicMessageChannel (主要是传递字符串和一些半结构体的数据)

BasicMessageChannel Android端

private void initBasicMessageChannel() {
        BasicMessageChannel<Object> basicMessageChannel = new BasicMessageChannel<>(getFlutterView(), BASIC_CHANNEL, StandardMessageCodec.INSTANCE);
        //主动发送消息到flutter 并接收flutter消息回复
            basicMessageChannel.send("send basic message", (object)-> {
                Log.e(TAG, "receive reply msg from flutter:" + object.toString());
            });

        //接收flutter消息 并发送回复
        basicMessageChannel.setMessageHandler((object, reply)-> {
            Log.e(TAG, "receive msg from flutter:" + object.toString());
            reply.reply("reply:got your message");

        });

    }

BasicMessageChannel Flutter端

  static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec());
//发送消息到原生客户端 并且接收到原生客户端的回复
  Future<String> sendMessage() async {
    String reply = await basicChannel.send('this is flutter');
    print("receive reply msg from native:$reply");
    return reply;
  }

  //接收原生消息 并发送回复
  void receiveMessage() async {
    basicChannel.setMessageHandler((msg) async {
      print("receive from Android:$msg");
      return "get native message";
    });

上面例子中用到的编解码器为StandardMessageCodec ,例子中通信都是String,用StringCodec也可以。

以上就是Flutter提供三种platform和dart端的消息通信方式。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Platform Channels (平台通道)
  • Platform Channels 使用
  • 1.MethodChannel的使用
    • 原生客户端写法(以Android 为例)
      • MethodChannel-Flutter 端
      • EventChannel
        • EventChannel 原生客户端写法
          • EventChannel Flutter端
          • BasicMessageChannel
            • BasicMessageChannel Android端
              • BasicMessageChannel Flutter端
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档