您好,登錄后才能下訂單哦!
本篇內容介紹了“Android集成Flutter的過程”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Android 集成Flutter
1, Hello Flutter
2, 引入 Flutter 模塊
3,使用Flutter
3.1 添加依賴
3.2 運行Flutter頁面
3.2.1 添加Flutter頁面
4,Flutter APK 解析
5,踩過的坑
Flutter 作為 Google 開源的新一代跨平臺、高性能 UI 框架,旨在幫助開發者高效地構建出跨平臺的、UI 與交互體驗一致的精美應用,推出后一直倍受開發者的青睞。
當需要開發一個全新的應用時,我們可以很方便地從零開始,完全使用 Flutter 進行開發。但如果是針對一個現有的應用,需要引入 Flutter 技術,顯然使用 Flutter 全部重寫一遍是不現實的。幸運的是,Flutter 很好地支持了以獨立頁面、甚至是 UI 片段的方式集成到現有的應用中,即所謂的混合開發模式。本文主要從一個 Android 開發的視角,談談 Android 平臺下, Flutter 的混合開發與構建。
對于這門技術,使用過的應該絕大多數都會說好;沒用過的推薦嘗試一下,跑個 Demo 體驗體驗,有可能它就是你需要學習和掌握的最后一門新技術了。回過頭來,Flutter 究竟有什么獨特的魅力讓它能從一眾技術中脫穎而出呢?總結一下,主要有以下幾點:
跨平臺:可以做到一套代碼完美適配 Android、iOS 平臺,未來還會覆蓋更多平臺,大大節省了開發人力與維護成本,同時擁有出色的跨端 UI 表現一致性。
高效開發:SDK 提供了豐富的 UI 組件,開箱即用;聲明式的 UI 構建方式,大大減少出錯率;Debug 模式提供熱重載能力,可實時預覽代碼變更,不需要重新編譯安裝。
高性能:采用自建渲染引擎,獨立于系統并可單獨優化;區別于 RN、WEEX,沒有中間層轉換的額外開銷;Release 模式下代碼編譯為 AOT 指令,運行高效。
受益于以上的核心優勢,Flutter 推出后圈了很多移動開發者的粉,各互聯網大廠也紛紛將其作為一項基礎技術進行研究。在 Flutter 初期,其應用場景主要是從 0 構建一個全新 App,對混合開發的支持很不友好。但作為一門跨平臺的技術框架,到底還是需要依賴原生平臺提供的諸多系統能力,此外還有眾多現存原生 App 躍躍欲試,因此在這個需求背景下,混合開發的支持與完善至今已發展得越來越好,下面我們就用一個簡單的示例開始 Android 端的 Flutter 混合開發與構建之旅。
要在一個已有的 Android Project 中使用 Flutter,需要引入一個 Flutter Module。在 Android Studio(需要確保 Flutter 插件已經成功安裝并啟用)中打開現有 Android 工程,通過使用 File > New > New Module… 菜單,我們可以新創建一個 Flutter 模塊或是導入一個外部的 Flutter 模塊。
這里以最簡單的 Android App 項目為例,導入 Flutter 模塊。在 Flutter 模塊導入成功之后,原工程文件、結構都會發生一些變化,主要有:
settings.gradle 文件新增了以下內容。其實就是執行對應 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 外,Flutter Gradle 插件自動引入了額外幾個子 Module。
說明如下:
flutter_module:指代要引入的目標 Flutter Module,不會 apply Android 相關的任何插件,主要是包含 Flutter 相關源碼、資源、依賴等。
flutter:為 Flutter Gradle 插件引入的 Android Library Module;主要負責編譯 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 平臺側的代碼、資源作為一個 Library Module 引入到項目中一起參與構建。如果要查看 flutter_module 引入了哪些 Plugin,可以查看其對應目錄下的 .flutter-plugins 與 .flutter-plugins-dependencies 文件,這兩個文件是執行 flutter pub get 時生成的,記錄了插件的本地文件目錄、依賴信息等。
首先,需要在 App 模塊的build.gradle腳本文件中添加對Flutter工程的依賴,只有這樣 Flutter 模塊才會參與到整個應用的構建中來,我們也才能夠在 App 模塊中調用到 Flutter 提供的 Java 層 API。
dependencies { implementation project(':flutter') }
我們可以選擇使用Activity、Fragment 或者 View 來承載 Flutter 的 UI,這里主要介紹前面兩種方式,并假設flutter_module中已經通過runApp方法渲染了一個widget。
Flutter Activity
首先,我們介紹下使用 Flutter Activity的方式。使用io.flutter.embedding.android.FlutterActivity類可以很方便的啟動一個 Flutter Activity,當然我們也可以繼承它并擴展自己的邏輯。
FlutterActivity .withNewEngine() .build(context) .also { startActivity(it) } Flutter Fragment
另外一種就是 Flutter Fragment方式。可以使用FlutterFragmentActivity或者FlutterFragment來添加 Flutter UI 片段:a. 使用FlutterFragmentActivity可以自動創建并添加一個FlutterFragment;b. 手動創建FlutterFragment后添加到目標 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()
不論是開發 Plugin 還是業務邏輯,平臺層與 Flutter 層通信是必不可少的,為此就需要使用到MethodChannel。平臺層通過MethodChannel請求調用 Flutter 層 API 時,數據在經過打包編碼后,通過 JNI、DartVM 傳到 Flutter 層解碼后使用;待結果計算完成后,又會重新打包編碼,經過 DartVM、JNI 傳回到 Native 層;同理,在 Flutter 層請求調用平臺層的 API 時,數據處理是一致的,只是流轉方向相反。通過這種方式,平臺層與 Flutter 層就建立了一個雙向的、異步的通信通道。
在下面的示例代碼中,Native 層使用dev.flutter.example/counter創建一個MethodChannel,并設置 Handler 接收 Dart 的遠程方法調用 incrementCounter,并調用 reportCounter 將結果回傳,如下所示。
channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") channel.setMethodCallHandler { call, _ -> when (call.method) { "incrementCounter" -> { count++ channel.invokeMethod("reportCounter", count) } } }
Dart 層使用相同的名稱創建 MethodChannel,并設置 Handler 處理回調結果,隨后調用 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(); } }
在上面的示例中,我們是通過手動創建 MethodChannel 進行通信的,這在進行簡單通信的場景是沒問題的,但在通信接口 API 比較復雜的情況就不是很適用了。一是繁瑣,因為我們需要手寫大量的打包、拆包代碼;二是容易出錯。
這個時候就輪到 Pigeon 大顯身手了。Pigeon 是一個官方推出的代碼生成工具,可以生成類型安全的雙向通信 API 接口,具體可以參考官方的 例子,Pigeon官方鏈接。
我們已經了解了如何在現有 Android 項目中引入并使用 Flutter,接下來我們再來探究一下 Flutter APK 的結構,看看 Flutter Tools 在這個 APK 包內到底打包了哪些東西。下面兩圖分別為 Debub 模式和 Release 模式下構建出來的 Flutter APK 包結構,忽略了非 Flutter 相關的項。
可以看到兩個模式下的 APK 結構大致相同,區別如下:
lib/{arch}/libflutter.so:
為對應架構的 Flutter Engine 共享庫,負責 Flutter 渲染、JNI 通信、DartVM。如果不需要對應架構的版本,通過 abiFilters 可以 Exclude 掉。
lib/{arch}/libapp.so:
只存在于 Release 模式下,共享庫中包含 Dart AOT 生成的二進制指令和數據。在運行時,Flutter Engine 通過 Dynamic Load 的方式,從共享庫中讀取對應的可執行機器指令以及數據。
assets/flutter_assets:Flutter 引用到的相關資源:
fonts:包含字體庫。
FontManifest.json:
引用到的字體庫清單文件,json 格式,所有使用到的字體、以及字體文件在 flutter_assets 下的路徑。
AssetManifest.json:其他資源清單文件,json 格式,為所有資源名稱到資源路徑的映射,Flutter 在加載某一項資源時,會通過這個配置清單找到對應路徑的資源進行讀取后加載。
kernel_blob.bin、isolate_snapshot_data、vm_snapshot_data:只存在于 Debug 模式下,分別為 DartVM 字節碼與數據,其作用類似于 libapp.so,只是存在形式、打包方式不同。在 Debug 模式下,Flutter Tools 將指令和數據分別打包,主要是為了熱重載(HotReload)服務的,而在 Release 模式下是統一打包成共享庫。
Flutter 混合開發使得開發者可以漸進式地進行 Flutter 開發與遷移,是 Flutter 寄生于原生平臺至關重要的一環,不過在接入Flutter的過程中,也出現了一些問題:
路由管理復雜:這里面包括 Flutter 層內部的頁面路由管理以及 Flutter 與原生的混合棧管理。前者在 Navigator 2.0 API 中已經得到了很好的完善與支持,但后者仍面臨著諸多限制與不足,需要改進。目前項目中還未涉及到后者這種很復雜的業務場景,因此對這一塊的研究比較少,感興趣的同學可以了解一下諸如 flutter_boost 此類的開源解決方案。
生命周期不對應:Android 的組件一般都會有自己的生命周期,Flutter 的 Widget State 也有一套自己的生命周期,但這兩者其實并不是一一對應的。比如原生的 Activity 頁面雖然已經被 Finish 并 Destroy 掉了,但 Flutter 層的頁面并不一定會隨之而被 Dispose,尤其是在使用 Cache Flutter Engine 的時候。Flutter 頁面是可以脫離原生頁面而存在的,它們可以被動態地 Attach 和 Detach,Attach 時會觸發重新渲染,Detach 時 UI 相關的所有操作都會 Pending 直到重新被 Attach。所以在混合開發中,業務邏輯不應該過度依賴 Widget State 的一些生命周期方法,因為它們可能會被延后執行從而導致一些奇怪的 Bug。
“Android集成Flutter的過程”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。