Flutter技術剖析(Flutter技術

      網友投稿 1126 2025-03-31

      flutter已經開源了三年,但是最近兩年才開始在開源社區活躍起來,尤其是最近還發布了Preview 1版本。作為可以實現一套代碼同時在iOS、Android平臺上運行的又一個新的UI框架,flutter提供給開發者的不僅僅是高速實現,還有高質量、流暢的UI。在加上其開源免費,因而受到很多移動開發者和企業的喜愛。

      跨平臺開發經常需要面對的一個問題是,Android 或 iOS 的平臺不同,那么使用同一套代碼,它們是怎么進行渲染的呢?本文以“hello_flutter”為例,首先介紹 Flutter 的設計原則,然后討論定制和優化,并為對 Flutter 感興趣的開發人員提供了一些可遵循的步驟。

      Flutter基礎

      架構

      Flutter框架從上到下包含三個不同的層:框架、引擎和嵌入器。Flutter 的框架使用 Dart 實現,提供了 Material 風格的小部件、Cupertino 風格的小部件(用于 iOS)、文本 / 圖像 / 按鈕小部件、渲染、動畫、手勢等。該層的核心代碼包含了 flutter 代碼庫的包和 sky_engine 代碼庫的包(dart:ui 庫提供了 flutter 框架和引擎之間的接口),例如 io、async 和 ui 包。

      Flutter 的引擎是用 C++ 實現的,并包含了 Skia、Dart 和 Text。Skia 是一個開源的 2D 圖形庫,為各種硬件和軟件平臺提供通用 API。它是谷歌 Chrome、Chrome OS、Android、Mozilla Firefox、Firefox OS 等產品的圖形引擎。支持的平臺包括 Windows7+、macOS 10.10.5+、iOS8+、Android4.1+、Ubuntu14.04+ 等。

      引擎的 Dart 部分主要包括 Dart 運行時和垃圾回收(GC)。如果 Flutter 在調試模式下運行,則還包括 JIT(Just in Time)支持,而如果是在發布模式下,則通過 AOT(Ahead of Time)將 Dart 代碼編譯為原生的“arm”代碼,這個時候就沒有 JIT。Text 是指以下的文本渲染庫:libtxt 庫(用于字體選擇和分隔線),派生自 minikin 和 HartBuzz(用于字形和圖形)。Skia 充當渲染后端,在 Android 上使用 FreeType 渲染,在 iOS 上使用 Fuchsia 和 CoreGraphics 渲染。

      嵌入器可將 Flutter 嵌入到各種平臺中。它的主要任務是渲染 Surface 設置、線程設置和插件。我們可以看到,Flutter 的平臺相關層是最小的,其中平臺(例如 iOS)只提供畫布,其余與渲染相關的邏輯發生在 Flutter 內部,從而實現良好的跨平臺一致性。

      工程結構

      一個完整的Flutter工程的項目結構如下圖所示:其中,“ios”是 iOS 的代碼,使用 CocoaPods 來管理依賴項,“android”是 Android 的代碼,使用 Gradle 來管理依賴項,“lib”是 Dart 代碼,使用 pub 來管理依賴項。pub 中與 Cocoapods 的 Podfile 和 Podfile.lock 相對應的分別是 pubspec.yaml 和 pubspec.lock。

      模式

      Flutter 支持常見的模式,包括調試、發布和分析,但它們之間存在一些區別。

      Flutter 的調試模式對應于 Dart 的 JIT 模式,也稱為檢查模式或慢模式,并支持 iOS 和 Android 的設備和模擬器。在這個模式下,可以啟用斷言功能,包括所有調試信息、服務擴展和調試輔助工具,如“observatory”。這個模式針對快速開發進行了優化,但并沒有針對運行速度、程序包大小或部署進行過優化。在這種模式下,采用了基于 JIT 的編譯技術,支持流行的亞秒級有狀態熱重載。

      Flutter 的發布模式對應于 Dart 的 AOT 模式,該模式的目標是部署到最終用戶的設備上,支持真實設備而不是模擬器。在此模式下,所有斷言都被禁用,為了盡可能多地刪除調試信息,還會禁用所有調試工具。這個模式針對快速啟動、快速運行和包大小進行了優化,同時禁止所有調試輔助和服務擴展。

      Flutter 的分析模式與發布模式類似,只是添加了對服務擴展和跟蹤的支持,并最小化使用跟蹤信息所需的依賴性。例如,“observatory”可以連到進程上。分析模式不支持模擬器,因為模擬器上的診斷不能代表實際性能。

      由于 Flutter 的分析模式和發布模式在編譯方面沒有差異,因此本文僅討論調試模式和發布模式。

      實際上,使用 Flutter 開發的 iOS 或 Android 項目仍然是標準的 iOS 或 Android 項目。Flutter 通過在 BuildPhase 中添加 shell 來生成并嵌入 App.framework 和 Flutter.framework(iOS),并通過 Gradle 添加 flutter.jar 和 vm/isolate_snapshot_data/instr(Android)來編譯相關代碼并將其嵌入到原生應用程序中。因此,本文主要討論 Flutter 引入的構建和運行原則。盡管編譯目標包括 arm、x64、x86 和 arm64,但它們的原理都很類似,所以本文僅討論與 arm 相關的原則。(如果沒有特殊描述,Android 默認為 armv7。)

      iOS 的代碼編譯和執行

      下面就以 iOS 的代碼編譯和執行為例,講解iOS項目在發布模式下的編譯和執行過程。

      在發布模式下,iOS 項目的 Dart 代碼構建過程如下圖所示:在圖中,gen_snapshot 是 Dart 編譯器,它使用搖樹優化技術(類似于可生成最小包的依賴樹邏輯,因此在 Flutter 中禁用 Dart 支持的反射)生成匯編形式的機器碼,然后通過編譯工具鏈(如 xcrun)生成最終 App.framework。換句話說,對于所有的 Dart 代碼,包括業務邏輯代碼和第三方軟件包代碼,它們所依賴的 Flutter 框架(Dart)代碼最終會變成 App.framework。可以通過“engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc”來查看相關的邏輯。

      事實上,與 Android 類似,App.framework 也包括了四個部分:kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData 和 kDartIsolateSnapshotInstructions。為什么 iOS 使用 App.framework 而不是像 Android 那樣的四個文件?由于 iOS 系統的限制,Flutter 引擎無法在運行時將內存頁標記為可執行,而在 Android 下則可以。

      Flutter.framework 對應于 Flutter 架構中的引擎和嵌入器。實際上,Flutter.framework 位于 flutter 代碼庫的 /bin/cache/artifacts/engine/ios * 中,默認是從谷歌的代碼庫中提取的。當需要自定義變更時,可以使用 Ninja 系統下載和構建與引擎相關的代碼。

      Flutter 相關代碼的最終產物是 App.framework(由 Dart 代碼生成)和 Flutter.framework(引擎)。在 Xcode 項目中,Generated.xcconfig 描述了與 Flutter 相關的環境配置信息,在 Runner 項目設置中新增的 xcode_backend.sh 實現了一個副本(從 flutter 框架代碼庫到 Runner 項目根目錄下的 Flutter 目錄)和 Flutter.framework 的嵌入以及 App.framework 的編譯和嵌入。

      所以,在iOS上最終生成的 Runner.app 中與 Flutter 相關的文件如下:flutter_assets 是 Flutter 資源,App.framework 和 Flutter.framework 是代碼,位于 Frameworks 目錄中。

      與 Flutter 相關的渲染、事件和通信處理的邏輯如下圖表示:

      在調試模式下,Flutter 的編譯結構與發布模式中的編譯結構類似。差異主要表現在兩點:Flutter.framework和App.framework。?Flutter.framework?在調試模式下,框架包含 JIT 支持,而在發布模式下沒有 JIT 支持。?App.framework?與 Flutter.framework 不同,App.framework 是原生機器碼,與 AOT 模式中的 Dart 代碼對應,而在 JIT 模式下,App.framework 只有幾個簡單的 API,Dart 代碼存在于 snapshot_blob.bin 文件中。這部分代碼的快照是帶有簡單標記的源代碼的腳本快照。所有的注釋和空格字符都被移除,常量被規格化,不存在機器代碼、搖樹優化或代碼混淆。

      Android 的代碼編譯和執行

      除了一些與平臺相關的功能之外,Android 其他邏輯(例如對應于 AOT 的發布模式和對應于 JIT 的調試模式)與 iOS 非常相似,只需注意兩個關鍵差異。

      ####發布模式下編譯 在發布模式下,Android Flutter 項目中的 Dart 代碼結構如下:其中,vm/isolate_snapshot_data/instr 是 arm 指令,引擎會在運行時加載它們并將其標記為可執行。vm_snapshot_data/instr 用于初始化 DartVM,調用入口為 Dart_Initialize(Dart_api.h)。isolate_snapshot_data/instr 對應于創建新隔離的 App 代碼,調用入口為 Dart_CreateIsolate(Dart_api.h)。

      Flutter.jar 類似于 iOS 的 Flutter.framework,包括引擎代碼(Flutter.jar 中的 libflutter.so)以及一組將 Flutter 嵌入到 Android 中的類和接口(FlutterMain、FlutterView、FlutterNativeView 等)。事實上,flutter.jar 位于 Flutter 代碼庫的 /bin/cache/artifacts/engine/android* 中,默認從谷歌代碼庫中拉取。當需要自定義更改時,可以使用 Ninja 系統下載引擎源代碼來生成 flutter.jar。

      其中,其 APK 結構如下:在全新安裝 APK 之后,使用時間戳(結合 versionCode 與 packageinfo 的 lastUpdateTime)來決定是否將 Flutter 相關文件復制到本地 app 數據目錄。復制的內容如下:

      isolate/vm_snapshot_data/instr 最終位于 app 本地數據目錄中,該目錄是可寫的。因此,可以通過下載和替換這些快照就可以完成 app 的替換和更新。

      下圖顯示了發布模式下的執行流程:

      與 iOS 的情況一樣,Android 中的調試模式和發布模式之間的區別主要在于以下兩個組件:flutter.jar和app源碼。?flutter.jar?這里的區別與之前針對 iOS 所描述的完全相同。?app 代碼?app 代碼位于 flutter_assets 下的 snapshot_blob.bin 中,就像 iOS 一樣。

      在介紹完 Flutter 有關 iOS 和 Android 的編譯原理后,我們將重點介紹如何配置 Flutter 及其引擎,以進行自定義和優化。因為 Flutter 使用了敏捷開發模式,所以當前出現的問題在未來可能就不是問題。因此,以下部分不著重于如何解決問題,而是著重于不同類型的場景,這些場景體現了 Flutter 自定義和優化方面的原則。

      Flutter 是一個復雜的系統。除了上面提到的三層架構,它還包括 Flutter Android Studio(Intellij)插件、pub 代碼庫管理和其他各種組件。不過,定制和優化通常與 Flutter 的工具鏈有關,代碼位于 Flutter 代碼庫的 flutter_tools 包中。我們現在將分別介紹如何針對 Android 和 iOS 進行自定義。

      自定義Android

      自定義 Android 涉及 flutter.jar、libflutter.so(在 flutter.jar 中)、gen_snapshot、flutter.gradle 和 flutter_tools。在自定義 Flutter 時,需要注意以下事項:?1. 將 Android 中的目標設置為 armeabi。?這是構建過程的一部分,邏輯是在 flutter.gradle 中定義的。如果要應用程序通過 armeabi 支持 armv7/arm64,必須修改 Flutter 的默認邏輯。

      由于 Gradle 本身的特性,這部分代碼需要在修改后才能生效。?2. 將 Android 設置為在啟動時默認使用第一個可啟動的 Activity。?這部分與 flutter_tools 有關,修改如下:

      原則上,使用“flutter run/build/analyze/test/upgrade”這樣的命令實際上運行的是 Flutter 腳本(flutter_repo_dir/bin/flutter),然后再通過這個腳本運行 flutter_tools.snapshot(由 packages/flutter_tools 生成)。邏輯如下:

      很明顯,如果要重建 flutter_tools,可以刪除 flutter_repo_dir/bin/cache/flutter_tools.stamp(以便重新生成它),或者注釋掉 if/fi(每次都重新生成)。?3. 在調試模式下發布 Flutter.?如果你發現 Flutter 在開發中出現延遲,并且猜測這可能是由邏輯或調試模式引起的,那么可以在發布模式下構建 APK,或者將 Flutter 強制改為為發布模式, 修改內容如下:

      自定義 iOS

      與自定義 iOS 相關的內容包括 Flutter.framework、gen_snapshot、xcode_backend.sh 和 flutter_tools。在自定義 Flutter 時,需要注意以下事項:

      1. 在優化期間重復替換 Flutter.framework 導致的重新編譯

      這部分的邏輯與構建有關,位于 xcode_backend.sh 中。為了確保每次都能獲得正確的 Flutter.framework,Flutter 每次都會根據配置查找并替換 Flutter.framework(請參閱 Generated.xcconfig 配置)。不過,這會導致重新編譯依賴于這個框架的項目代碼。必要的修改如下:2. 在調試模式下發布 Flutter?要進行這個自定義,請將 Generated.xcconfig 中的 FLUTTER_BUILD_MODE 更改為“Release”,將 FLUTTER_FRAMEWORK_DIR 更改為與“Release”對應的路徑。?3. 設置對 armv7 的支持?有關此方案的原始文檔,請參閱 https://github.com/flutter/engine/wiki/iOS-Builds-Supporting-ARMv7。

      事實上,Flutter 本身在 iOS 中支持 armv7,但目前官方還沒有提供支持,因此必須修改相關邏輯,如下所示:

      a. 生成默認邏輯:

      Flutter.framework(arm64)

      b. 修改 Flutter,以便每次都可以重建 flutter_tools。修改 build_aot.Dart 和 mac.Dart,將 iOS 的相關 arm64 更改為 armv7,并將 gen_snapshot 更改為 i386。?可以通過以下命令生成 i386 的 gen_snapshot:

      ./flutter/tools/gn?--runtime-mode=debug?--ios?--ios-cpu=armninja?-C?out/ios_debug_arm

      這里有一種隱含的邏輯:構造 gen_snapshot 的預定義宏(x86_64/__ i386 等)。目標 gen_snapshot 的結構和最終的 App.framework 結構必須保持一致。也就是說,使用 x86_64->x86_64->arm64 或 i386->i386->armv7。

      c. 在 iPhone4S 上,當 gen_snapshot 生成不受支持的 SDIV 命令時會發生 EXC_BAD_INSTRUCTION(EXC_ARM_UNDEFINED)錯誤,這可以通過向 gen_snapshot(位于 build_aot.Dart 中)添加參數“——no-use-integer-division”來解決。

      d.“lipo -create”在 a 步驟和 b 步驟中生成的 Flutter.framework,以便生成支持 armv7 和 arm64 的 Flutter.framework。

      e. 修改 Flutter.framework 中的 Info.plist,并刪除如下:

      UIRequiredDeviceCapabilitiesarm64

      同樣,你必須在 App.framework 上執行相同的操作,以避免受到 AppStore 中應用程序細化的影響。

      調試 Flutter 工具

      在調試模式下構建 APK 時,如果你想知道 Flutter 的特定執行邏輯,可以采用以下方法:

      a. 了解 flutter_tools 命令的參數。b. 將 packages/flutter_tools 作為 Dart 項目打開,并添加新的“Dart Command Line App”配置?將 Dart 文件設置為“flutter_tools.Dart”,將工作目錄設置為 Flutter 項目的路徑,并將 Program 參數設置為先前獲得的參數。

      自定義和調試引擎

      請考慮以下情形。假設我們基于 Flutter beta v0.3.1 定制和開發服務,為了確保穩定性,SDK 在某段時間內不會升級。同時,Flutter v0.3.1 的 master 分支上修改了一個 bug,標記為 fix_bug_commit。你將如何應對這種情況?

      1.Flutter beta v0.3.1 將其相應的引擎代碼提交指定為 09d05a389。 關于這部分知識可以參閱:flutter/bin/internal/engine.version。

      Flutter技術剖析(Flutter技術)

      獲取引擎代碼。

      由于 master 代碼是在第二步中獲得的,我們需要的是與特定提交相對應的代碼(09d05a389),所以需要從這次提交拉取一個新分支:custom_beta_v0.3.1。

      在 custom_beta_v0.3.1(commit:09d05a389)上運行“gclient sync”,獲取與 flutter beta v0.3.1 相對應的所有引擎代碼。

      使用“git cherry-pick fix_bug_commit”將 master 的變更同步到 custom_beta_v0.3.1。如果變更依賴了最新依賴項,則可能會發生編譯錯誤。

      運行以下命令應用與 iOS 相關的變更:

      ./flutter/tools/gn?--runtime-mode=debug?--ios?--ios-cpu=armninja?-C?out/ios_debug_arm./flutter/tools/gn?--runtime-mode=release?--ios?--ios-cpu=armninja?-C?out/ios_release_arm./flutter/tools/gn?--runtime-mode=profile?--ios?--ios-cpu=armninja?-C?out/ios_profile_arm./flutter/tools/gn?--runtime-mode=debug?--ios?--ios-cpu=arm64ninja?-C?out/ios_debug./flutter/tools/gn?--runtime-mode=release?--ios?--ios-cpu=arm64ninja?-C?out/ios_release./flutter/tools/gn?--runtime-mode=profile?--ios?--ios-cpu=arm64ninja?-C?out/ios_profile

      要調試 Flutter.framework 源代碼,請使用以下命令:

      ./flutter/tools/gn?--runtime-mode=debug?--unoptimized?--ios?--ios-cpu=arm64ninja?-C?out/ios_debug_unopt

      用生成的文件替換 Flutter 中的 Flutter.framework 和 gen_snapshot,這樣就可以調試引擎源代碼。 7. 最后,運行以下命令應用與 Android 相關的變更

      ./flutter/tools/gn?--runtime-mode=debug?--android?--android-cpu=armninja?-C?out/android_debug./flutter/tools/gn?--runtime-mode=release?--android?--android-cpu=armninja?-C?out/android_release./flutter/tools/gn?--runtime-mode=profile?--android?--android-cpu=armninja?-C?out/android_profile

      然后,使用生成的文件替換 flutter/bin/cache/artifacts/engine/android* 下的 gen_snapshot 和 flutter.jar,以便生成 Android 的 arm 和 debug/release/profile 文件包。

      移動開發

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:食品企業生產管理制度(食品生產企業制度的食品安全管理制度)
      下一篇:wps白色背景設置為淺綠色 以保護眼睛和防止眼睛疲勞
      相關文章
      亚洲免费一级视频| 丁香婷婷亚洲六月综合色| 亚洲AV无码一区二区三区牛牛| 亚洲视频在线免费播放| 亚洲综合在线视频| 精品日韩亚洲AV无码| 久久精品国产亚洲av麻豆色欲| 亚洲成人动漫在线| 精品亚洲aⅴ在线观看| 亚洲精品成人网站在线播放| 亚洲国产日韩在线人成下载 | 亚洲小视频在线播放| 亚洲特级aaaaaa毛片| 亚洲国产成人精品无码区在线秒播| 亚洲精品在线不卡| 色噜噜亚洲男人的天堂| 亚洲欧美日韩综合久久久| 亚洲精品久久无码| 国产亚洲男人的天堂在线观看 | 亚洲成av人片天堂网老年人| 亚洲成A∨人片天堂网无码| 亚洲男人的天堂在线va拉文| 伊伊人成亚洲综合人网7777| 亚洲国产成人一区二区精品区 | 99亚洲精品卡2卡三卡4卡2卡| 亚洲A∨午夜成人片精品网站| 亚洲精品和日本精品| 亚洲色大成网站www永久一区| 亚洲AV无码一区二区三区DV | 亚洲国产精品美女| 亚洲中文无码永久免费| 亚洲av无码一区二区三区天堂| 亚洲第一视频在线观看免费| 国产亚洲日韩在线三区| 亚洲AV日韩AV永久无码绿巨人| 亚洲福利视频网站| 亚洲精品无码久久| 亚洲人成影院在线无码观看| 亚洲AV午夜福利精品一区二区| 亚洲精品中文字幕乱码影院| 亚洲人成人网毛片在线播放|