当前位置: 移动技术网 > 移动技术>移动开发>Android > Flutter学习笔记(29)--Flutter如何与native进行通信

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

2019年10月13日  | 移动技术网移动技术  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网