zoukankan      html  css  js  c++  java
  • Flutter学习笔记(29)--Flutter如何与native进行通信

    如需转载,请注明出处: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进行注册

    package com.example.flutter_demo;
    
    import android.content.Context;
    
    import io.flutter.plugin.common.MethodCall;
    import io.flutter.plugin.common.MethodChannel;
    import io.flutter.plugin.common.PluginRegistry;
    
    public class TestPlugin implements MethodChannel.MethodCallHandler {
        public static String CHANNELNAME = "channel_name";//每一个通信通道的唯一标识,在整个项目内唯一!!!
        private static MethodChannel methodChannel;
        private Context context;
    
        public TestPlugin(Context context) {
            this.context = context;
        }
    
        public static void registerWith(PluginRegistry.Registrar registrar){
            methodChannel = new MethodChannel(registrar.messenger(),CHANNELNAME);
            TestPlugin instance = new TestPlugin(registrar.activity());
            methodChannel.setMethodCallHandler(instance);
        }
    
        @Override
        public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
            if (methodCall.method.equals("method_key")){
                result.success("what is up man???");
            }
        }
    }

    注: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端

    package com.example.flutter_demo;
    
    import android.os.Bundle;
    import io.flutter.app.FlutterActivity;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    
    public class MainActivity extends FlutterActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        TestPlugin.registerWith(this.registrarFor(TestPlugin.CHANNELNAME));
      }
    }

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

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

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatefulWidget{
      @override
      State<StatefulWidget> createState() {
        // TODO: implement createState
        return new MyAppState();
      }
    
    }
    
    class MyAppState extends State<MyApp> {
      var _textContent = 'welcome to flutter word';
    
      Future<Null> _changeTextContent() async{
        //channel_name每一个通信通道的唯一标识,在整个项目内唯一!!!
        const platfom = const MethodChannel('channel_name');
        try {
          //method_key是插件TestPlugin中onMethodCall回调匹配的key
          String resultValue = await platfom.invokeMethod('method_key');
          setState(() {
            _textContent = resultValue;
          });
        }on PlatformException catch (e){
          print(e.toString());
        }
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return new MaterialApp(
          theme: new ThemeData(
            primaryColor: Colors.white,
          ),
          debugShowCheckedModeBanner: false,
          title: 'demo',
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('Demo'),
              leading: Icon(Icons.menu,size: 30,),
              actions: <Widget>[
                Icon(Icons.search,size: 30,)
              ],
            ),
            body: new Center(
              child: new Text(_textContent),
            ),
            floatingActionButton: new FloatingActionButton(onPressed: _changeTextContent,child: new Icon(Icons.adjust),),
          ),
        );
      }
    }

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

    我们看一下最终的效果:

                    

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

    • EventChannel

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

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

    public class EventChannelPlugin implements EventChannel.StreamHandler {
    
        private Handler handler;
        private static final String CHANNEL = "com.example.flutter_battery/stream";
        private int count = 0;
    
        public static void registerWith(PluginRegistry.Registrar registrar) {
            // 新建 EventChannel, CHANNEL常量的作用和 MethodChannel 一样的
            final EventChannel channel = new EventChannel(registrar.messenger(), CHANNEL);
            // 设置流的处理器(StreamHandler)
            channel.setStreamHandler(new EventChannelPlugin());
        }
    
        @Override
        public void onListen(Object o, EventChannel.EventSink eventSink) {
            // 每隔一秒数字+1
            handler = new Handler(message -> {
                // 然后把数字发送给 Flutter
                eventSink.success(++count);
                handler.sendEmptyMessageDelayed(0, 1000);
                return false;
            });
            handler.sendEmptyMessage(0);
    
        }
    
        @Override
        public void onCancel(Object o) {
            handler.removeMessages(0);
            handler = null;
            count = 0;
        }
    }

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

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

    class _MyHomePageState extends State<MyHomePage> {
      // 创建 EventChannel
      static const stream = const EventChannel('com.example.flutter_battery/stream');
    
      int _count = 0;
    
      StreamSubscription _timerSubscription;
    
      void _startTimer() {
        if (_timerSubscription == null)
           // 监听 EventChannel 流, 会触发 Native onListen回调
          _timerSubscription = stream.receiveBroadcastStream().listen(_updateTimer);
      }
    
      void _stopTimer() {
        _timerSubscription?.cancel();
        _timerSubscription = null;
        setState(() => _count = 0);
      }
    
      void _updateTimer(dynamic count) {
        print("--------$count");
        setState(() => _count = count);
      }
    
      @override
      void dispose() {
        super.dispose();
        _timerSubscription?.cancel();
        _timerSubscription = null;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Container(
            margin: EdgeInsets.only(left: 10, top: 10),
            child: Center(
              child: Column(
                children: [
                  Row(
                    children: <Widget>[
                      RaisedButton(
                        child: Text('Start EventChannel',
                            style: TextStyle(fontSize: 12)),
                        onPressed: _startTimer,
                      ),
                      Padding(
                          padding: EdgeInsets.only(left: 10),
                          child: RaisedButton(
                            child: Text('Cancel EventChannel',
                                style: TextStyle(fontSize: 12)),
                            onPressed: _stopTimer,
                          )),
                      Padding(
                        padding: EdgeInsets.only(left: 10),
                        child: Text("$_count"),
                      )
                    ],
                  )
                ],
              ),
            ),
          ),
        );
      }
    }

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

    • BasicMessageChannel

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

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // 省略其他代码...
        
        messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE);
        messageChannel.
            setMessageHandler(new MessageHandler<String>() {
                @Override
                public void onMessage(String s, Reply<String> reply) {
                    // 接收到Flutter消息, 更新Native
                    onFlutterIncrement();
                    reply.reply(EMPTY_MESSAGE);
                }
            });
    
        FloatingActionButton fab = findViewById(R.id.button);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 通知 Flutter 更新
                sendAndroidIncrement();
            }
        });
    }
    
    private void sendAndroidIncrement() {
        messageChannel.send(PING);
    }
    
    private void onFlutterIncrement() {
        counter++;
        TextView textView = findViewById(R.id.button_tap);
        String value = "Flutter button tapped " + counter + (counter == 1 ? " time" : " times");
        textView.setText(value);
    }

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

    class _MyHomePageState extends State<MyHomePage> {
      static const String _channel = 'increment';
      static const String _pong = 'pong';
      static const String _emptyMessage = '';
      static const BasicMessageChannel<String> platform =
          BasicMessageChannel<String>(_channel, StringCodec());
    
      int _counter = 0;
    
      @override
      void initState() {
        super.initState();
        // 设置消息处理器
        platform.setMessageHandler(_handlePlatformIncrement);
      }
    
      // 如果接收到 Native 的消息 则数字+1
      Future<String> _handlePlatformIncrement(String message) async {
        setState(() {
          _counter++;
        });
        // 发送一个空消息
        return _emptyMessage;
      }
    
      // 点击 Flutter 中的 FAB 则发消息给 Native
      void _sendFlutterIncrement() {
        platform.send(_pong);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('BasicMessageChannel'),
          ),
          body: Container(
              child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Expanded(
                child: Center(
                  child: Text(
                      'Platform button tapped $_counter time${_counter == 1 ? '' : 's'}.',
                      style: const TextStyle(fontSize: 17.0)),
                ),
              ),
              Container(
                padding: const EdgeInsets.only(bottom: 15.0, left: 5.0),
                child: Row(
                  children: <Widget>[
                    Image.asset('assets/flutter-mark-square-64.png', scale: 1.5),
                    const Text('Flutter', style: TextStyle(fontSize: 30.0)),
                  ],
                ),
              ),
            ],
          )),
          floatingActionButton: FloatingActionButton(
            onPressed: _sendFlutterIncrement,
            child: const Icon(Icons.add),
          ),
        );
      }
    }

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

  • 相关阅读:
    c++ 函数中的部分代码执行一次
    如何限制对象只能建立在堆上或者栈上
    FFMPEG Qt视频播放器
    C/C++中带可变参数的函数
    柔性数组
    压缩图片网站
    vscode存盘时格式化
    两个i标签之间有缝隙
    node 中process进程argv,argv0,execArgv,execPath
    chalk插件 使终端输出的字带颜色
  • 原文地址:https://www.cnblogs.com/upwgh/p/11662640.html
Copyright © 2011-2022 走看看