溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android集成Flutter的過程

發(fā)布時間:2021-08-24 17:19:27 來源:億速云 閱讀:144 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“Android集成Flutter的過程”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • Android 集成Flutter

    • 1, Hello Flutter

    • 2, 引入 Flutter 模塊

    • 3,使用Flutter

      • 3.1 添加依賴

      • 3.2 運行Flutter頁面

      • 3.2.1 添加Flutter頁面

    • 4,F(xiàn)lutter APK 解析

      • 5,踩過的坑

      Android 集成Flutter

      Flutter 作為 Google 開源的新一代跨平臺、高性能 UI 框架,旨在幫助開發(fā)者高效地構(gòu)建出跨平臺的、UI 與交互體驗一致的精美應(yīng)用,推出后一直倍受開發(fā)者的青睞。

      當(dāng)需要開發(fā)一個全新的應(yīng)用時,我們可以很方便地從零開始,完全使用 Flutter 進(jìn)行開發(fā)。但如果是針對一個現(xiàn)有的應(yīng)用,需要引入 Flutter 技術(shù),顯然使用 Flutter 全部重寫一遍是不現(xiàn)實的。幸運的是,F(xiàn)lutter 很好地支持了以獨立頁面、甚至是 UI 片段的方式集成到現(xiàn)有的應(yīng)用中,即所謂的混合開發(fā)模式。本文主要從一個 Android 開發(fā)的視角,談?wù)?Android 平臺下, Flutter 的混合開發(fā)與構(gòu)建。

      1, Hello Flutter

      對于這門技術(shù),使用過的應(yīng)該絕大多數(shù)都會說好;沒用過的推薦嘗試一下,跑個 Demo 體驗體驗,有可能它就是你需要學(xué)習(xí)和掌握的最后一門新技術(shù)了?;剡^頭來,F(xiàn)lutter 究竟有什么獨特的魅力讓它能從一眾技術(shù)中脫穎而出呢?總結(jié)一下,主要有以下幾點:

      • 跨平臺:可以做到一套代碼完美適配 Android、iOS 平臺,未來還會覆蓋更多平臺,大大節(jié)省了開發(fā)人力與維護(hù)成本,同時擁有出色的跨端 UI 表現(xiàn)一致性。

      • 高效開發(fā):SDK 提供了豐富的 UI 組件,開箱即用;聲明式的 UI 構(gòu)建方式,大大減少出錯率;Debug 模式提供熱重載能力,可實時預(yù)覽代碼變更,不需要重新編譯安裝。

      • 高性能:采用自建渲染引擎,獨立于系統(tǒng)并可單獨優(yōu)化;區(qū)別于 RN、WEEX,沒有中間層轉(zhuǎn)換的額外開銷;Release 模式下代碼編譯為 AOT 指令,運行高效。

      受益于以上的核心優(yōu)勢,F(xiàn)lutter 推出后圈了很多移動開發(fā)者的粉,各互聯(lián)網(wǎng)大廠也紛紛將其作為一項基礎(chǔ)技術(shù)進(jìn)行研究。在 Flutter 初期,其應(yīng)用場景主要是從 0 構(gòu)建一個全新 App,對混合開發(fā)的支持很不友好。但作為一門跨平臺的技術(shù)框架,到底還是需要依賴原生平臺提供的諸多系統(tǒng)能力,此外還有眾多現(xiàn)存原生 App 躍躍欲試,因此在這個需求背景下,混合開發(fā)的支持與完善至今已發(fā)展得越來越好,下面我們就用一個簡單的示例開始 Android 端的 Flutter 混合開發(fā)與構(gòu)建之旅。

      2, 引入 Flutter 模塊

      要在一個已有的 Android Project 中使用 Flutter,需要引入一個 Flutter Module。在 Android Studio(需要確保 Flutter 插件已經(jīng)成功安裝并啟用)中打開現(xiàn)有 Android 工程,通過使用 File > New > New Module… 菜單,我們可以新創(chuàng)建一個 Flutter 模塊或是導(dǎo)入一個外部的 Flutter 模塊。

      Android集成Flutter的過程

      這里以最簡單的 Android App 項目為例,導(dǎo)入 Flutter 模塊。在 Flutter 模塊導(dǎo)入成功之后,原工程文件、結(jié)構(gòu)都會發(fā)生一些變化,主要有:

      • settings.gradle 文件新增了以下內(nèi)容。其實就是執(zhí)行對應(yīng) Flutter 模塊下 .android/include_flutter.groovy 腳本文件,該步驟會引入一個名為 Flutter 的 Android Library Module,同時還會引入 Flutter 模塊所依賴的所有插件。

      setBinding(new Binding([gradle: this])) 
      evaluate(new File( 
          settingsDir.parentFile, 
          'flutter_module/.android/include_flutter.groovy' 
      )) 
      include ':flutter_module' 
      project(':flutter_module').projectDir = new File('../flutter_module')

      在引入 Flutter 模塊之前,項目中僅有 app 一個 Module;而在引入之后,可以看到除了原有的 app Module 外,F(xiàn)lutter Gradle 插件自動引入了額外幾個子 Module。

      Android集成Flutter的過程

      說明如下:

      • flutter_module:指代要引入的目標(biāo) Flutter Module,不會 apply Android 相關(guān)的任何插件,主要是包含 Flutter 相關(guān)源碼、資源、依賴等。

      • flutter:為 Flutter Gradle 插件引入的 Android Library Module;主要負(fù)責(zé)編譯 flutter_module 及其依賴的第三方 Package、Plugin 的 Dart 代碼,以及打包 Flutter 資源等。

      • device_info:為 Flutter Gradle 插件自動引入的 Flutter Android Plugin Library Module,這是因為一開始我在 flutter_module 的 pubspec.yaml 文件中添加了對 device_info 這個插件的依賴。Flutter Gradle 工具會將 flutter_module 依賴到的所有插件其 Android 平臺側(cè)的代碼、資源作為一個 Library Module 引入到項目中一起參與構(gòu)建。如果要查看 flutter_module 引入了哪些 Plugin,可以查看其對應(yīng)目錄下的 .flutter-plugins 與 .flutter-plugins-dependencies 文件,這兩個文件是執(zhí)行 flutter pub get 時生成的,記錄了插件的本地文件目錄、依賴信息等。

      3,使用Flutter

      3.1 添加依賴

      首先,需要在 App 模塊的build.gradle腳本文件中添加對Flutter工程的依賴,只有這樣 Flutter 模塊才會參與到整個應(yīng)用的構(gòu)建中來,我們也才能夠在 App 模塊中調(diào)用到 Flutter 提供的 Java 層 API。

      dependencies { 
        implementation project(':flutter') 
      }
      3.2 運行Flutter頁面
      3.2.1 添加Flutter頁面

      我們可以選擇使用Activity、Fragment 或者 View 來承載 Flutter 的 UI,這里主要介紹前面兩種方式,并假設(shè)flutter_module中已經(jīng)通過runApp方法渲染了一個widget。

      Flutter Activity

      首先,我們介紹下使用 Flutter Activity的方式。使用io.flutter.embedding.android.FlutterActivity類可以很方便的啟動一個 Flutter Activity,當(dāng)然我們也可以繼承它并擴(kuò)展自己的邏輯。

      FlutterActivity 
        .withNewEngine() 
        .build(context) 
        .also { 
          startActivity(it) 
        }
      Flutter Fragment

      另外一種就是 Flutter Fragment方式??梢允褂肍lutterFragmentActivity或者FlutterFragment來添加 Flutter UI 片段:a. 使用FlutterFragmentActivity可以自動創(chuàng)建并添加一個FlutterFragment;b. 手動創(chuàng)建FlutterFragment后添加到目標(biāo) Activity 中。

      val flutterFragment = FlutterFragment.withNewEngine() 
            .dartEntrypoint(getDartEntrypointFunctionName()) 
            .initialRoute(getInitialRoute()) 
            .appBundlePath(getAppBundlePath()) 
            .flutterShellArgs(FlutterShellArgs.fromIntent(intent)) 
            .handleDeeplinking(shouldHandleDeeplinking()) 
            .renderMode(renderMode) 
            .transparencyMode(transparencyMode) 
            .shouldAttachEngineToActivity(shouldAttachEngineToActivity()) 
            .build<FlutterFragment>() 
      fragmentManager 
            .beginTransaction() 
            .add( 
                 FRAGMENT_CONTAINER_ID, 
                 flutterFragment, 
                 TAG_FLUTTER_FRAGMENT 
                ) 
             .commit()
       3.2.2 平臺層和 Flutter 層通信

      不論是開發(fā) Plugin 還是業(yè)務(wù)邏輯,平臺層與 Flutter 層通信是必不可少的,為此就需要使用到MethodChannel。平臺層通過MethodChannel請求調(diào)用 Flutter 層 API 時,數(shù)據(jù)在經(jīng)過打包編碼后,通過 JNI、DartVM 傳到 Flutter 層解碼后使用;待結(jié)果計算完成后,又會重新打包編碼,經(jīng)過 DartVM、JNI 傳回到 Native 層;同理,在 Flutter 層請求調(diào)用平臺層的 API 時,數(shù)據(jù)處理是一致的,只是流轉(zhuǎn)方向相反。通過這種方式,平臺層與 Flutter 層就建立了一個雙向的、異步的通信通道。

      在下面的示例代碼中,Native 層使用dev.flutter.example/counter創(chuàng)建一個MethodChannel,并設(shè)置 Handler 接收 Dart 的遠(yuǎn)程方法調(diào)用 incrementCounter,并調(diào)用 reportCounter 將結(jié)果回傳,如下所示。

      channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") 
      channel.setMethodCallHandler { call, _ -> 
           when (call.method) { 
               "incrementCounter" -> { 
                    count++ 
                    channel.invokeMethod("reportCounter", count) 
               } 
           } 
      }

      Dart 層使用相同的名稱創(chuàng)建 MethodChannel,并設(shè)置 Handler 處理回調(diào)結(jié)果,隨后調(diào)用 incrementCounter 方法請求 counter。

      final _channel = MethodChannel('dev.flutter.example/counter'); 
      _channel.setMethodCallHandler(_handleMessage); 
      _channel.invokeMethod('incrementCounter'); 
       
      Future<dynamic> _handleMessage(MethodCall call) async { 
          if (call.method == 'reportCounter') { 
            _count = call.arguments as int; 
            notifyListeners(); 
          } 
        }

      在上面的示例中,我們是通過手動創(chuàng)建 MethodChannel 進(jìn)行通信的,這在進(jìn)行簡單通信的場景是沒問題的,但在通信接口 API 比較復(fù)雜的情況就不是很適用了。一是繁瑣,因為我們需要手寫大量的打包、拆包代碼;二是容易出錯。

      這個時候就輪到 Pigeon 大顯身手了。Pigeon 是一個官方推出的代碼生成工具,可以生成類型安全的雙向通信 API 接口,具體可以參考官方的 例子,Pigeon官方鏈接。

      4,F(xiàn)lutter APK 解析

      我們已經(jīng)了解了如何在現(xiàn)有 Android 項目中引入并使用 Flutter,接下來我們再來探究一下 Flutter APK 的結(jié)構(gòu),看看 Flutter Tools 在這個 APK 包內(nèi)到底打包了哪些東西。下面兩圖分別為 Debub 模式和 Release 模式下構(gòu)建出來的 Flutter APK 包結(jié)構(gòu),忽略了非 Flutter 相關(guān)的項。

      Android集成Flutter的過程

      可以看到兩個模式下的 APK 結(jié)構(gòu)大致相同,區(qū)別如下:

      • lib/{arch}/libflutter.so:為對應(yīng)架構(gòu)的 Flutter Engine 共享庫,負(fù)責(zé) Flutter 渲染、JNI 通信、DartVM。如果不需要對應(yīng)架構(gòu)的版本,通過 abiFilters 可以 Exclude 掉。

      • lib/{arch}/libapp.so:只存在于 Release 模式下,共享庫中包含 Dart AOT 生成的二進(jìn)制指令和數(shù)據(jù)。在運行時,F(xiàn)lutter Engine 通過 Dynamic Load 的方式,從共享庫中讀取對應(yīng)的可執(zhí)行機(jī)器指令以及數(shù)據(jù)。

      • assets/flutter_assets:Flutter 引用到的相關(guān)資源:

      • fonts:包含字體庫。

      • FontManifest.json:引用到的字體庫清單文件,json 格式,所有使用到的字體、以及字體文件在 flutter_assets 下的路徑。

      • AssetManifest.json:其他資源清單文件,json 格式,為所有資源名稱到資源路徑的映射,F(xiàn)lutter 在加載某一項資源時,會通過這個配置清單找到對應(yīng)路徑的資源進(jìn)行讀取后加載。

      • kernel_blob.bin、isolate_snapshot_data、vm_snapshot_data:只存在于 Debug 模式下,分別為 DartVM 字節(jié)碼與數(shù)據(jù),其作用類似于 libapp.so,只是存在形式、打包方式不同。在 Debug 模式下,F(xiàn)lutter Tools 將指令和數(shù)據(jù)分別打包,主要是為了熱重載(HotReload)服務(wù)的,而在 Release 模式下是統(tǒng)一打包成共享庫。

      5,踩過的坑

      Flutter 混合開發(fā)使得開發(fā)者可以漸進(jìn)式地進(jìn)行 Flutter 開發(fā)與遷移,是 Flutter 寄生于原生平臺至關(guān)重要的一環(huán),不過在接入Flutter的過程中,也出現(xiàn)了一些問題:

      • 路由管理復(fù)雜:這里面包括 Flutter 層內(nèi)部的頁面路由管理以及 Flutter 與原生的混合棧管理。前者在 Navigator 2.0 API 中已經(jīng)得到了很好的完善與支持,但后者仍面臨著諸多限制與不足,需要改進(jìn)。目前項目中還未涉及到后者這種很復(fù)雜的業(yè)務(wù)場景,因此對這一塊的研究比較少,感興趣的同學(xué)可以了解一下諸如 flutter_boost 此類的開源解決方案。

      • 生命周期不對應(yīng):Android 的組件一般都會有自己的生命周期,F(xiàn)lutter 的 Widget State 也有一套自己的生命周期,但這兩者其實并不是一一對應(yīng)的。比如原生的 Activity 頁面雖然已經(jīng)被 Finish 并 Destroy 掉了,但 Flutter 層的頁面并不一定會隨之而被 Dispose,尤其是在使用 Cache Flutter Engine 的時候。Flutter 頁面是可以脫離原生頁面而存在的,它們可以被動態(tài)地 Attach 和 Detach,Attach 時會觸發(fā)重新渲染,Detach 時 UI 相關(guān)的所有操作都會 Pending 直到重新被 Attach。所以在混合開發(fā)中,業(yè)務(wù)邏輯不應(yīng)該過度依賴 Widget State 的一些生命周期方法,因為它們可能會被延后執(zhí)行從而導(dǎo)致一些奇怪的 Bug。

      “Android集成Flutter的過程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

      向AI問一下細(xì)節(jié)

      免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

      AI