Flutter与原生如何进行通信

  用Flutter这样的跨平台技术进行商业级项目开发时,几乎不可避免的需要和Native进行通信,比如不同平台的底层服务如电量变化、网络连接变化、陀螺仪、传感器等等都有各自不同的实现,以及编译期的一些配置,比如包名、版本号、第三方依赖APPKEY等都需要通过原生的方法去获取。所以得学一哈Native和Flutter的通信方式是非常有必要的

前话

Native和Flutter之间可以通过Platform Channels APIs进行通信,
Flutter定义了三种不同类型的Channel:


  • MethodChannel:用于传递方法调用(method invocation),适用于一次性通信

  • BasicMessageChannel:用于传递字符串和半结构化的消息,持续通信可回复

  • EventChannel: 用于事件流的发送(event streams),持续通信不可回复

一、MethodChannel通信

MethodChannel是最常用的Native和Flutter的通信方式,主要用于Flutter调用Native端方法,如调用Native相机功能

为了便于理解,这里我介绍一个前两天在公司项目中实际用到MethodChannel的场景。基本现在Android APP都会涉及到多渠道打包问题,针对不同的渠道,我们可能会在编译脚本(build文件)中进行一些不同的配置。但是Flutter如何才能拿到Android Gradle中的配置信息呢?其实很简单,也就是我们可以先通过Android代码拿到BuildConfig中的数据,再通过Flutter与Android进行通信拿到数据。这种一次性的调用Android中代码的情况就需要用到MethodChannel

1.1 Android端

创建MethodChannel,通过setMethodCallHandler接收Flutter端的方法调用

    private val Method_Channel = "com.example.flutter_test_call/methodChannel"
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        //1.创建android端的MethodChannel
        val methodChannel = MethodChannel(flutterEngine.dartExecutor, Method_Channel)
        //2.通过setMethodCallHandler响应Flutter端的方法调用
        methodChannel.setMethodCallHandler { call, result ->
            //判断调用的方法名
            if(call.method.equals("testMethodCall")){
                //获取传递的参数
                Log.e("test", "Have received Test Method Call :${call.arguments}")
                //返回结果给Flutter端
                result.success(BuildConfig.DEBUG.toString() + " " +"yes")
            }
        }
    }
1.2 Flutter端

Flutter端同样也有MethodChannel,可以通过类似方法调用的方式,调用Native层的方法,并拿到返回值

  String Method_Channel = "com.example.flutter_test_call/methodChannel";
  Future<void> testMethodCall() async {
    //1.创建Flutter端的MethodChannel
    MethodChannel _methodChannel = MethodChannel(Method_Channel);
    //2.通过invokeMethod调用Native方法,拿到返回值
    String debugString = await _methodChannel.invokeMethod("testMethodCall", "give me debug info");
    print('test debugString=$debugString');
  }

这里要注意两点:

  • ChannelName要与Native端保持一致(即com.example.flutter_test_call/methodChannel)
  • MethodName要与Native端保持一致(即testMethodCall)

二、BasicMessageChannel通信

BasicMessageChannel用于Native和Flutter互相发送消息,一方给另一方发送消息,收到消息之后给出回复

2.1 Android给Flutter发送消息
2.1.1 Android端

创建一个BasicMessageChannel,通过send方法发送消息

private val Basic_Method_Channel = "com.example.flutter_test_call/basicMethodChannel"

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    //1.创建android端的BasicMessageChannel
    val basicMethodChannel = BasicMessageChannel(flutterEngine.dartExecutor, Basic_Method_Channel, StringCodec.INSTANCE)
    //2.向Flutter端发送消息
    basicMethodChannel.send("Hello, this is BasicMethodChannel Send msg!") {         
            replyString ->            
               Log.i("test", "收到了Flutter端发来的回复:$replyString")
    }
}

发送的消息会以二进制的形式进行处理,所以要针对不同类型的数据进行二进制编码

编码类型 消息格式
BinaryCodec 发送二进制消息
JSONMessageCodec 发送Json格式消息
StandardMessageCodec 发送基本型数据
StringCodec 发送String类型消息
2.1.2 Flutter端

Flutter端同样也有BasicMessageChannel,通过setMessageHandler接收并回复消息

String Basic_Method_Channel = "com.example.flutter_test_call/basicMethodChannel";
initBasicMethodCall(){
  //1.创建Flutter端的BasicMessageChannel
  basicMessageChannel = BasicMessageChannel(Basic_Method_Channel, StringCodec());
  //2.接收来自Native的消息,并向Native回复
  basicMessageChannel.setMessageHandler((message) {
    print('test Android端发来的消息:$message');
    return Future.value('黄河黄河,我是长江');
  });
}
2.2 Flutter给Android发送消息
2.2.1 Flutter端

在Flutter端我们也可以通过send方法向Native发送消息,方法的返回值就是Native端的消息回复

注意flutter和Native的通信都是异步的

testBasicMethodCall() async {
  String replyString =  await basicMessageChannel?.send("Android, 我是Flutter端!");
  print('test Android端回复的的消息:$replyString');
}
2.2.2 Android端

android端通过setMessageHandler设置消息处理器,处理来自Dart的消息,收到消息后通过reply进行回复

    basicMethodChannel.setMessageHandler { message, reply ->
        Log.v("test", "收到了Flutter端发来的消息:$message")
        //通过reply进行回复
        reply.reply("长江长江,我是黄河~")
    }

三、EventChannel通信

EventChannel用于从Native向Flutter发送通知事件,例如Flutter通过其监听Android的重力感应变化等。与MethodChannel不同,EventChannel是Native到Flutter的单向调用,调用是一对多的,类似Android的BrodcastReceiver

3.1 Android端

创建一个EventChannel,在StreamHandler#onLister回调中获取EventSink引用并保存,当重力感应发送变化时,通过eventSink.success向Flutter端发送消息

private val Event_Channel = "com.example.flutter_test_call/eventChannel"
var eventSink: EventChannel.EventSink? = null
fun registerEventChannel(binaryMessenger: BinaryMessenger){
     //1.创建Android端的EventChannel
    val eventChannel = EventChannel(binaryMessenger, Event_Channel)
    //2.在StreamHandler#onLister回调中获取EventSink引用并保存
    eventChannel.setStreamHandler(object : EventChannel.StreamHandler{
        override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
            eventSink = events
        }

        override fun onCancel(arguments: Any?) {
            eventSink = null
        }

    })
}

fun getNetWorkType(context : Context){
    var connectivityManager : ConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    var info = connectivityManager.activeNetworkInfo
    if(info != null){
        if(info?.type == ConnectivityManager.TYPE_WIFI){
            //3.调用eventSink.success向Flutter端发送消息
            eventSink?.success("WIFI")
        }else if(info.type == ConnectivityManager.TYPE_MOBILE){
            eventSink?.success("Mobile")
        }else{
            eventSink?.success("Unkonw")
        }
    }else{
        //3.报错后调用eventSink.error向Flutter端发送消息
        eventSink?.error("error network", "error network", "error network")
    }

}
3.2 Flutter端

Flutter端接收消息如下所示,要注意使用EventChannel时需要在页面销毁时取消监听,防止内存泄漏

String eventChannel = 'com.example.flutter_test_call/eventChannel';
StreamSubscription streamSubscription;
testEventChannel(){
  //1.创建Flutter端EventChannel
  EventChannel _eventChannel = EventChannel(eventChannel);
  //2.EventChannel#receiveBroadcastStream注册listener,建立监听
  streamSubscription = _eventChannel.receiveBroadcastStream()
    .listen((event) {
      print("Network Status : $event");
  }, onError: (errorcode){
     print('errorcode: $errorcode');
  });
}

@override
void dispose() {
  // TODO: implement dispose
  super.dispose();
  //3.页面销毁时需要取消监听,防止内存泄漏
  streamSubscription?.cancel();
}

申明:禁用于商业用途,如若转载,请附带原文链接。https://www.jianshu.com/p/e376574e2402蟹蟹~

PS: 写文不易,觉得没有浪费你时间,请给个关注和点赞~ 😁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354