寫在前頭
在之前的一篇文章《Flutter的探索與實踐》中關於Flutter如何構建到現有專案這一節沒有詳細說,這篇文章將會介紹Flutter在混合專案中的構建和整合方面踩過的坑以及解決方案。
目標
扇貝現有的專案是原生混合React Native,並且是以元件化的架構存在,現在想在一些頁面上用Flutter進行重構,想要在新的Flutter專案上整合以前的所有程式碼肯定是不現實的,同時又不想將Flutter專案直接侵入到我們的專案結構中去,於是我們選擇將重構好的Flutter程式碼單獨編譯成aar,以元件的形式被主工程依賴。
這樣做的好處是顯而易見的: 對Flutter進行探索開發的同學可以在自己的Flutter工程內編寫dart程式碼,獨立執行除錯,釋出版本的時候打包成aar整合到主工程中讓寫native程式碼的同學接入,兩方可以協同工作,不會產生耦合。
探索過程
下面的構建和整合是以Android專案為例。
首先用Android Studio或者命令列新建一個Flutter 工程。
得到如下專案結構
在命令列輸入命令flutter build apk
會編譯生成apk檔案,位於build/app/outputs/apk/release/
資料夾下。
將apk解壓縮後就可以看到裡面的結構組成。
這個apk裡的產物實際上是在Android的app/build.gradle構建程式碼裡引入了Flutter的構建程式碼。
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
複製程式碼
通過閱讀flutter構建原始碼我們發現在構建apk檔案的時候,會將需要的檔案構建到apk中。
1.assets資料夾
assets資料夾下面有flutter_assets資料夾、flutter_shared資料夾、isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr檔案。
- flutter_assets裡是flutter工程產生的assets檔案
- flutter_shared裡是封裝在flutter.jar裡面的處理字元編碼的ICU庫
- isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr為特定平臺的資料和指令
2.lib資料夾
lib資料夾下是特定平臺(arm或者x86)的so檔案。
flutter在Android平臺下會預設生成arm-v7架構的的so庫,flutter.gradle原始碼中會根據target-platform
屬性判斷平臺動態生成對應的so,官方註釋目前flutter只支援在debug模式下生成x86的so。
扇貝應用整合了一部分三方的so庫,而且只選用了arm架構的so庫,x86的裝置只佔到1%左右,因此下面的操作都是預設為arm架構。對需要x86 so的同學下文會做說明。
抽取aar
上面通過編譯命令得到了apk,那想要打包成aar,理論上只要把app/build.gradle中的apply plugin: 'com.android.application'
修改為apply plugin: 'com.android.library'
,同時刪除applicationId "com.shanbay.flutterapp"
再次執行flutter build apk
命令,便可以得到app-release.aar
檔案。
整合到現有專案
我們將得到的aar檔案整合到現有的Android工程中供native的小夥伴使用,但是開啟flutter頁面卻閃退了,同時flutter報出了error,錯誤是說aar裡面缺少icudtl.dat檔案。
解壓縮aar檢視檔案結構,可以發現其中的問題。
在aar資料夾下的assets裡面缺少了flutter_shared資料夾,icudtl.dat檔案正是在該資料夾裡面,也就是說flutter.gradle在編譯流程中並沒有將icudtl.dat檔案打進aar包裡面,這一點從flutter庫的issue裡面得到了證實,我們的辦法是將apk裡面得到的flutter_shared資料夾手動copy到flutter工程中,再次編譯aar,這樣就可以得到有icudtl.dat的aar檔案。再次整合到Android專案中便可以成功執行,不會產生錯誤。總結
這個方案需要兩個步驟,第一步是先編譯成apk取得icudtl.dat檔案放入到工程中,第二步修改apply plugin: 'com.android.library'
再次編譯取得aar。
優點
- 完全依賴flutter自己的編譯流程,不會對其原始碼進行修改,侵入flutter編譯流程,定製化的成分較少,適用於絕大部分場景,同時隨著flutter更新,需要對這套流程做修改的可能性也比較小。
- 可以直接接入CI系統,最小修改。
- 在android工程中可以編寫sample程式碼,通過flavor構建在release aar時移出sample程式碼,不需要另外建立host工程執行除錯。