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提供 MethodChannel、EventChannel、BasicMessageChannel 三種方式

  1. 類似注冊監聽,發送的模式 原則
  2. 使用順序:先注冊,后發送,否則接收不到。尤其使用 MethodChannel、EventChannel 不符合該原則會拋出異常,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 ,分別為 StandardMessageCodec、JSONMessageCodec、StringCodecBinaryCodec。

  • 注意點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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,556評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,975評論 2 59
  • afinalAfinal是一個android的ioc,orm框架 https://github.com/yangf...
    wgl0419閱讀 6,360評論 1 9
  • 我立在陽臺前,窗外風雨飄搖,人流滾滾,生活依然以不變的步伐前行。當贊譽聲像雪片一樣紛紛飛來,那耀眼的黃綠色,風馳電...
    愛笑的美人魚閱讀 935評論 1 2
  • 前幾周都沒有記錄 因為去三亞過年了,然后回來后的那個周末不舒服,于是推遲到今天。 在三亞過年沒啥好說的,每年都會去...
    小梨渦妹閱讀 451評論 1 0