欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Flutter学习笔记(29)--Flutter如何与native进行通信

程序员文章站 2022-03-20 22:05:10
如需转载,请注明出处:Flutter学习笔记(29)--Flutter如何与native进行通信 前言:在我们开发Flutter项目的时候,难免会遇到需要调用native api或者是其他的情况,这时候就需要处理Flutter与native的通信问题,一般常用的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的文案修改。

我们看一下最终的效果:

Flutter学习笔记(29)--Flutter如何与native进行通信                 Flutter学习笔记(29)--Flutter如何与native进行通信

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通信的全部内容了,理解了以后其实很简单,上面的内容有一些我个人的理解,更深一层的还需要继续挖掘!