Flutter与Android混合开发及Platform Channel的使用

  1. 相对于单独开发Flutter应用,混合开发对于线上项目更具有实际意义,可以把风险控制到最低,也可以进行实战上线。所以介绍 集成已有项目

  2. 混合开发涉及原生Native和Flutter进行通信传输,还有插件编写,所以介绍 两端通信Flutter Platform Channel的使用

WanAndroid客户端简单Flutter版 Apk

集成已有项目

官方方案 | 闲鱼团队方案 | 头条团队方案

采用官方的实现方式,对个人和小团队相对简单易用,并没有实践闲鱼和头条等大项目的方式。个中利弊上述推荐均有提到。

开始

  1. 在已有项目根目录运行命令

    flutter create -t module my_flutter

    不一定非在项目下创建,兼顾ios,位置路径寻址对即可

  2. 在项目的settings.gradle 文件添加如下代码

    // MyApp/settings.gradle
    include ':app'                                     // assumed existing content
    setBinding(new Binding([gradle: this]))                                 // new
    evaluate(new File(                                                      // new
      settingsDir.parentFile,                                               // new
      'my_flutter/.android/include_flutter.groovy'                          // new
    ))                                                                      // new
    

    Binding飘红不予理会,强迫症的不用导包
    'my_flutter/.android/include_flutter.groovy'为my_flutter文件路径,报错找不到可写项目全路径'ChannelFlutterAndroid/my_flutter/.android/include_flutter.groovy'

  3. 在运行模块app的build.gradle添加依赖

    // MyApp/app/build.gradle
    dependencies {
        implementation project(':flutter')
    }
    
  4. 在原生Android端创建并添加flutterView

     val flutterView = Flutter.createView(this, lifecycle, "route1")
     val layoutParams = FrameLayout.LayoutParams(-1, -1)
     addContentView(flutterView, layoutParams)
    
  5. 在Flutter端入口操作

    void main() => runApp(_widgetForRoute(window.defaultRouteName));
    
    Widget _widgetForRoute(String route) {
      switch (route) {
        case 'route1':
          return SomeWidget(...);
        default:
          return Center(
            child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
          );
      }
    }
    

    至此,集成完毕

两端通信Flutter Platform Channel的使用

本文主要是介绍使用方式,闲鱼团队详尽的介绍了实现原理

Flutter提供 MethodChannelEventChannelBasicMessageChannel 三种方式

  1. 类似注册监听,发送的模式 原则
  2. 使用顺序:先注册,后发送,否则接收不到。尤其使用 MethodChannelEventChannel 不符合该原则会抛出异常,BasicMessageChannel方式只是收不到消息

方式一 MethodChannel

应用场景:Flutter端 调用 Native端

Flutter端代码:

 var result = await MethodChannel("com.simple.channelflutterandroid/method", 
                      StandardMethodCodec())
                      .invokeMethod("toast", {"msg": msg})

Android端代码:

  MethodChannel(flutterView, "com.simple.channelflutterandroid/method",
              StandardMethodCodec.INSTANCE)
             .setMethodCallHandler { methodCall, result ->
        when (methodCall.method) {
             "toast" -> {
                //调用传来的参数"msg"对应的值
                val msg = methodCall.argument<String>("msg")
                //调用本地Toast的方法
                Toast.makeText(cxt, msg, Toast.LENGTH_SHORT).show()
                //回调给客户端
                 result.success("native android toast success")
             }
            "other" -> {
               // ...
             }
             else -> {
               // ...
             }
         }
   }
  • 注意点1:两端需要对应一致的点
    方法名称:com.simple.channelflutterandroid/method,可以不采取包名,对应一致即可,不同的方式最好不要重复
    传参key:"msg"

  • 注意点2:以下为Flutter为例,Android同理
    StandardMethodCodec() 非必传,默认是 StandardMethodCodec(),是一种消息编解码器

    对于 MethodChannelEventChannel 两种方式,有两种编解码器,均实现自MethodCodec ,分别为 StandardMethodCodecJsonMethodCodec

    对于 BasicMessageChannel 方式,有四种编解码器,均实现自MessageCodec ,分别为 StandardMessageCodecJSONMessageCodecStringCodecBinaryCodec

  • 注意点3:setMethodCallHandler(),针对三种方式一一对应,属于监听
    MethodChannel - setMethodCallHandler()
    EventChannel - setStreamHandler()
    BasicMessageChannel - setMessageHandler()
    其中setStreamHandler()方式稍微特殊,具体查看上面提的闲鱼文章

  • 注意点4:FlutterView 实现 BinaryMessenger 接口

以上,以下两种方法不再赘述

方式二 EventChannel

应用场景:Native端 调用 Flutter端,方式稍微特殊

Flutter端代码:属于接收方

   EventChannel("com.simple.channelflutterandroid/event")
           .receiveBroadcastStream()
           .listen((event){
                   print("It is Flutter -  receiveBroadcastStream $event");
   })

注意 receiveBroadcastStream

Android端代码:属于发送方

   EventChannel(flutterView, "com.simple.channelflutterandroid/event")
         .setStreamHandler(object : EventChannel.StreamHandler {
               override fun onListen(o: Any?, eventSink: EventChannel.EventSink) {
                     eventSink.success("我是发送Native的消息")
               }

               override fun onCancel(o: Any?) {
                       // ...
               }
   })

方式三 BasicMessageChannel

应用场景:互相调用

两种使用方式,创建方式和格式不一样

第一种

Flutter端

    _basicMessageChannel = BasicMessageChannel("com.simple.channelflutterandroid/basic", 
                                                StringCodec())

    // 发送消息
    _basicMessageChannel.send("我是Flutter发送的消息");

    // 接收消息
    _basicMessageChannel.setMessageHandler((str){
        print("It is Flutter -  receive str");
    });

Android端

    val basicMessageChannel = BasicMessageChannel<String>(flutterView, "com.simple.channelflutterandroid/basic", 
                                                          StringCodec.INSTANCE)

    // 发送消息
    basicMessageChannel.send("我是Native发送的消息")

    // 接收消息
    basicMessageChannel.setMessageHandler { o, reply ->
        println("It is Native - setMessageHandler $o")
        reply.reply("It is reply from native")
    }

其中StringCodec(),四种方式可选

第二种

Flutter端

   static const BASIC_BINARY_NAME = "com.simple.channelflutterandroid/basic/binary";

   // 发送消息
   val res = await BinaryMessages.send(BASIC_BINARY_NAME, ByteData(256))

   // 接收消息
   BinaryMessages.setMessageHandler(BASIC_BINARY_NAME, (byteData){
       println("It is Flutter - setMessageHandler $byteData")
   });

Android端

   const val BASIC_BINARY_NAME = "com.simple.channelflutterandroid/basic/binary"

   // 发送消息
   flutterView.send(BASIC_BINARY_NAME,ByteBuffer.allocate(256));

   // 接收消息
   flutterView.setMessageHandler(BASIC_BINARY_NAME) { byteBuffer, binaryReply ->
       println("It is Native - setMessageHandler $byteBuffer")
       binaryReply.reply(ByteBuffer.allocate(256))
   }

此组合可以进行图片的传递

help 在操作中使用此方式传输数据总报错,所以只传了默认ByteBuffer。还是姿势不对???目前未发现,求大神指教

如有错误之处,万望各位指出!谢谢

其他

  1. Native端接收方收到消息后都能进行回传信息,在Flutter进行异步接收
    MethodChannel:result.success("我是回传信息");
    EventChannel:eventSink.success("我是回传信息");
    BasicMessageChannel:binaryReply.reply(-);- Flutter端:ByteData;Android端:ByteBuffer

Q&A

以上均为理想情况,过程中还是存在许多问题,以下提供参考

Q:使用命令行flutter create创建的project或者module,文件android/ios为隐藏打点开头的.android/.ios文件
A:所以有的时候会出现问题,寻找不到文件的情况

Q: flutter create module 和project的区别
A: ?? 目前没发现

Q:couldn't locate lint-gradle-api-26.1.2.jar for flutter project
A: https://stackoverflow.com/questions/52945041/couldnt-locate-lint-gradle-api-26-1-2-jar-for-flutter-project

Q: Could not resolve all files for configuration ':app:androidApis'.
Failed to transform file 'android.jar' to match attributes {artifactType=android-mockable-jar, returnDefaultValues=false} using transform MockableJarTransform
A:https://github.com/anggrayudi/android-hidden-api/issues/46

Q: Flutter Error: Navigator operation requested with a context that does not include a Navigator
A: https://github.com/flutter/flutter/issues/15919

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,815评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,704评论 2 59
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    wgl0419阅读 6,267评论 1 9
  • 我立在阳台前,窗外风雨飘摇,人流滚滚,生活依然以不变的步伐前行。当赞誉声像雪片一样纷纷飞来,那耀眼的黄绿色,风驰电...
    爱笑的美人鱼阅读 910评论 1 2
  • 前几周都没有记录 因为去三亚过年了,然后回来后的那个周末不舒服,于是推迟到今天。 在三亚过年没啥好说的,每年都会去...
    小梨涡妹阅读 420评论 1 0