使用 Flutter 之後,我們的 CPU 佔用率降了 50%
近年來,移動網際網路迅猛發展,業務需求頻繁更新,業務內容動態化需求急劇增加,純原生開發已經無法滿足業務快速增長的需求,因此誕生了多種跨平臺開發框架,如 H5+ 原生開發、React Native 和 Weex ,但這兩年最受開發者青睞的莫過於 Flutter。目前,很多應用都集成了 Flutter,我們團隊也在漲樂財富通上實現了完整 Flutter 的整合過程,以下篇幅會具體介紹整個整合過程。
漲樂 Flutter 實踐(以 iOS 為例)
此次實踐主要是為了驗證整個流程,為後續大規模應用 Flutter 做鋪墊,因此我們選擇了一個業務相對簡單的“技術論市”頁面進行改造,該頁面之前是 H5 實現的列表頁,點選欄目會跳轉到另一個 H5 頁面詳情頁。
改造之後,原生介面點選按鈕會開啟 Flutter 列表頁,點選 Flutter 頁面的欄目會跳轉到 H5 頁面,點選返回可依次返回到上一個介面。從圖中可以看出,整個流程的使用體驗非常流暢。
元件化整合
如何將 Flutter 程式碼整合進現有工程是我們遇到的第一個挑戰。 Flutter 官網 提供了一種解決方案,但存在以下缺陷:
1. 需要修改主工程的配置,入侵原有工程;
2. 工程執行需要 Flutter 環境,而在實際開發中並不是所有的團隊成員都會參與到 Flutter 的開發,安裝 Flutter 環境對於那些不需要 Flutter 開發的成員來說顯然不合理。
官網的方案行不同,我們必須另闢蹊徑。研究 Flutter 的編譯指令碼 xcode_backend.sh
發現,只要將 Flutter 編譯產物放入主工程就能執行 Flutter 模組。為了便於管理,漲樂財富通採用私有 pod 管理編譯產物的方式來整合 Flutter。
Flutter 工程的編譯產物包含三個部分,分別是:
1.App.framework:所有的 Dart 程式碼,包括業務程式碼和依賴的第三方 package 程式碼,在 Debug 模式下只是一個空殼,在 Release 模式下是所有程式碼生成的機器碼。
2.Flutter.framework:Flutter 的 SDK。
3.flutter_assets:Flutter 資原始檔,包括字型、圖片等。
建立私有 pod 用來管理這些編譯產物,podspec 的核心內容如下:
複製程式碼
s.source_files='htflutter_product_debug/Plugin/**/*' s.vendored_frameworks='Framework/*.framework','Framework/engine/*.framework' s.resources='Framework/flutter_assets'
主工程需要整合 Flutter 模組時,只需要在 podfile 中依賴該私有 pod 即可。
混合棧管理
引入 Flutter 模組後,需要考慮的就是如何管理混合棧。在現有應用中,已經存在原生 + 網頁的混合棧,如今引入了 Flutter 需要解決這三者如何巢狀。
混合棧管理的方案必須具備以下特點:
1. 原生、H5、Flutter 頁面三者能相互呼叫,並且使用者感覺不到差異;
2. 儘量減少資源消耗;
3. 每個頁面的生命週期保持完整。
為此,我們借鑑了閒魚團隊開源的混合棧管理方案,並與我們現有的路由管理方案相結合,在漲樂上實現了混合棧管理,具體架構圖如下:
- 頁面跳轉使用統一的路由管理。
漲樂財富通使用路由管理器來統一管理頁面。當需要開啟一個 Flutter 頁面時,只需要像原來一樣,傳送一個開啟 Flutter 的路由,並攜帶引數用來標識具體的頁面。路由管理器識別到是 Flutter 路由後會建立新的 WrapFlutterViewController
並壓棧。 WrapFlutterViewController
會使用 FlutterViewController
單例作為其子 VC,利用傳遞過來在引數在 FlutterViewController
內部開啟具體的 Flutter 頁面。
- 所有的 Flutter 頁面共用一個 Flutter 例項,iOS 使用
FlutterViewController
,Android 使用FlutterNativeView
。
共用一個 Flutter 例項,既可以使得 Flutter 頁面之間實現資料通訊和共享,也可以減少額外的資源消耗。因為每一個 Flutter 例項會啟動三個執行緒,分別是 UI 執行緒、GPU 執行緒和 IO 執行緒,只建立一個 Flutter 例項減少了資源的使用。
- 每一個 Flutter 頁面對應一個原生頁面。
每次 push/pop 一個 Flutter 頁面,一方面會操作 Flutter 例項內部的導航棧,另一方面在外部會 push/pop 一個原生的頁面,這樣可以確保 Flutter 頁面和原生頁面的同步。
自動化
整個 Flutter 的開發過程分為以下兩大步驟:
1. 編寫 dart 和 plugin 程式碼並生成 App.framework,Flutter_Asset 資料夾和 Flutter.framework;
2. 將編譯產物整合到 iOS 主工程;
自動化需要解決幾個關鍵問題:
1. 如何區分 debug 和 release 模式下的產物包
2. 自動化的流程應該如何控制
針對第一個問題,我們的解決辦法是建立兩個 repo, htflutter_product_debug
和 htflutter_product_release
,開發使用 debug 產物,生產使用 release 產物。
第二個問題,我們參考的是 CocoaPods 的 pod 釋出流程,將 Flutter 主工程作為一個私有 repo 來看待,通過 tag 觸發指令碼生成產物,再 push 到 htflutter_product_debug
和 htflutter_product_release
。具體流程如下圖:
1. 首先 htcftflutter
是我們的 Flutter 主工程,包含所有的 Dart 原始碼和 plugin 程式碼。
2. 通過 tag 名觸發指令碼,編譯出兩種模式的產物,例如 tag: debug_1.0
則編譯出 debug prudoct。
3. 將產物推送到遠端產物 pod repo(這一步實際上類似 pod repo push)。這一步相對複雜一點,
首先需要 clone 遠端的產物 pod 到當前的某個臨時資料夾,然後將 Flutter 主工程中編譯的產物拷貝到臨時資料夾中,其中包含 App.framework,flutter_assets 資料夾以及 Flutter.framework,另外還有 plugin 相關檔案。前面三個都好辦,直接拷貝即可,plugin 比較麻煩,plugin 的程式碼通過 package 的形式引入到工程中,並不在 Flutter 主工程,需要從 .flutter-plugins
檔案中讀取到各個 plugin 到路徑,然後到對應到路徑進行拷貝。拷貝完成之後,再通過指令碼完成 git
的相關操作即可,最後 push 完成,刪除臨時資料夾,這樣 htcftflutter
不感知整個指令碼執行過程。
4.iOS 主工程整合 Flutter 產物 pod,預設情況下 podfile
中依賴 htflutter_product_debug
, 主工程打 release tag 時,觸發指令碼將依賴修改成 htflutter_product_release
,並執行 pod update
。
相關指令碼如下:
複製程式碼
tool =HTTool.new(mode)//debug/release tool.build_ios()# 編譯產物 tool.clone_flutter_product_repo()#clone product repo tool.copy_products()# 拷貝產物 tool.copy_plugin_code()# 拷貝 plugin 原生程式碼 tool.updateSpecVersion()# 更新版本 tool.push_flutter_product_repo()#pushtoproduct repo tool.remove_product_repo()#delete product repo after push
降級策略
Flutter 還處於快速迭代發展的階段,正式上線可能存在不確定的風險,為此我們設計了具體的降級方案,應對 Flutter 發生異常的情況。
1. 應用啟動時,伺服器會下發 Flutter 降級配置表,key 是需要降級的 Flutter 頁面路徑,value 是需要執行的降級路由操作;
2. 路由管理器響應 Flutter 路由時,會首先判斷需要開啟的 Flutter 頁面是否需要降級,若需要,則會執行配置表中的路由操作,降級到網頁;反之則正常跳轉到 Flutter 頁面。
實踐結果
1. 安裝包大小
引入 Flutter 之前,漲樂財富通的安裝包為 94MB,引入之後大小為 100MB,發現增大了 6MB,這其中主要是引入了 Flutter 的 SDK,增加的大小在可以接受的範圍。
2.FPS 和 GPU
從上圖可以看出,Flutter 的 FPS 接近 60,和原生效果基本一致,而 H5 的 FPS 在 50 左右,遠不如 Flutter 優秀。兩者的 GPU 使用率基本相同。
3. 記憶體
記憶體表現方面,H5 頁面使用的記憶體要小於 Flutter。
4.CPU
從 CPU 的佔用率來看,Flutter 佔用的 CPU 要遠遠小於 H5 頁面。
總結
從我們的實踐結果來看,Flutter 在效能方面擁有絕對優秀的體驗,但 Flutter 的開發生態還不夠成熟,完全取代原生開發實現跨平臺為時尚早,但對於一些追求一致性、高效能的介面可以嘗試採用 Flutter 實現。我們漲樂財富通開發團隊也會持續跟進 Flutter 的發展,將 Flutter 推廣應用到更多業務場景中。
更多內容,請關注前端之巔。