您好,登錄后才能下訂單哦!
如何進行混合開發Flutter,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
Flutter 作為 Google 開源的新一代跨平臺、高性能 UI 框架,旨在幫助開發者高效地構建出跨平臺的、UI 與交互體驗一致的精美應用,推出后一直倍受開發者的青睞。
當需要開發一個全新的應用時,我們可以很方便地從零開始,完全使用 Flutter 進行開發。但如果是針對一個現有的應用,需要引入 Flutter 技術,顯然使用 Flutter 全部重寫一遍是不現實的。幸運的是,Flutter 很好地支持了以獨立頁面、甚至是 UI 片段的方式集成到現有的應用中,即所謂的混合開發模式。本文主要從一個 Android 開發的視角,談談 Android 平臺下, Flutter 的混合開發與構建。
相信現在應該很少會有移動端開發者不知道 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 時生成的,記錄了插件的本地文件目錄、依賴信息等。
注意:一個工程不能包含多個 Flutter Module,最多只能引入一個,這是由 Flutter 的 Gradle 插件決定的。
完成 Flutter 模塊的引入后,我們再來看看如何使用 Flutter。
首先需要在 App 模塊的build.gradle腳本文件中添加對Flutter工程的依賴,只有這樣 Flutter 模塊才會參與到整個應用的構建中來,我們也才能夠在 App 模塊中調用到 Flutter 提供的 Java 層 API。如下所示:
dependencies { implementation project(':flutter') }
我們可以選擇使用 Activity、Fragment 或者 View 來承載 Flutter 的 UI,這里主要介紹前面兩種方式,并假設flutter_module中已經通過runApp方法渲染了一個widget。
運行 Flutter Activity。使用io.flutter.embedding.android.FlutterActivity類可以很方便的啟動一個 Flutter Activity,當然我們也可以繼承它并擴展自己的邏輯。示例代碼如下:
FlutterActivity .withNewEngine() .build(context) .also { startActivity(it) }
運行 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()
平臺層和 Flutter 層通信。不論是開發 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 接口,具體可以參考官方的 Example,這里不再贅述。
Pigeon :https://flutter.dev/docs/development/platform-integration/platform-channels#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 模式下是統一打包成共享庫。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。