把Flutter作為一個模組接入到現有的Android工程,Flutter有官方推薦方案 Add Flutter to existing apps,通過這樣的工程配置,可以在debug支援HotReload,也可以輸出Release包供釋出。不過在使用過程中有一些需要調整的地方,特此記錄希望對大家能有借鑑意義。
工程目錄調整
flutter create -t module
命令會建立一個支援Flutter的Android Library,其中Android Library的目錄位於Flutter工程的隱藏目錄 .android/flutter 中, 一般情況下,我們會把Flutter程式碼和Android程式碼放在兩個git倉庫,通過submodule的方式進行依賴,可以把這個Library的程式碼copy到你的工程目錄下,同時修改flutter的資源目錄到你自己的相對路徑下:
flutter {
source ` your own flutter project directory `
}
另外需要Copy include_flutter.groovy 這個檔案到你的工程目錄下,修改相應的目錄新增對於Library的依賴。
armeabi支援
Flutter官方只提供了四種CPU架構的SO庫:armeabi-v7a、arm64-v8a、x86和x86-64。但是目前我們對接的兩個專案組分別是隻支援armeabi和只支援armeabi-v7a,所以需要對官方的jar包進行改造。官方SDK提供的jar包路徑在 $flutterRoot/bin/cache/artifacts/engine中,複製這幾個目錄下的armeabi-v7a中的so到armeabi路徑下:
- android-arm
- android-arm-dynamic-profile
- android-arm-dynamic-release
- android-arm-profile
- android-arm-release
可以通過如下指令碼實現:
unzip flutter.jar lib/armeabi-v7a/libflutter.so
mkdir lib/armeabi
cp lib/armeabi-v7a/libflutter.so lib/armeabi/libflutter.so
zip flutter.jar lib/armeabi-v7a/libflutter.so lib/armeabi/libflutter.so
複製程式碼
Library中的build.gradle中有一段是通過本地的一個gradle檔案新增flutter.jar的依賴:
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
複製程式碼
我們把flutter.gradle檔案以及我們剛才處理的flutter.jar檔案Copy到自己的工程路徑下,我自己的工程路徑配置如下:
project
│ app
└───flutter
│ │ build.gradle
│ │ flutter.gradle
│ │ include_flutter.groovy
│ └───flutter-jars
│ └───android-arm
| | flutter.jar
│ └───android-arm-dynamic-profile
| | flutter.jar
│ └───android-arm-dynamic-release
| | flutter.jar
| └───android-arm-profile
| | flutter.jar
| └───aandroid-arm-release
| | flutter.jar
│ settings.gradle
複製程式碼
將flutter.gradle 中jar檔案的路徑改為本地工程:
debugFlutterJar = new File(`flutter-jars/debug/flutter.jar`)
profileFlutterJar = new File(`flutter-jars/profile/flutter.jar`)
releaseFlutterJar = new File(`flutter-jars/release/flutter.jar`)
dynamicProfileFlutterJar = new File(`flutter-jars/dynamicProfile/flutter.jar`)
dynamicReleaseFlutterJar = new File(`flutter-jars/dynamicRelease/flutter.jar`)
複製程式碼
這樣打出的AAR就能同時支援兩種架構。
打包AAR問題
按照上面的配置,可以在工程中打出支援Debug HotReload和Release的包,不過在輸出AAR給別的業務模組使用時會報一個崩潰:
must be able to initialize the ICU context.
這是Android Gradle Plugin 3.+ 版本的一個bug,它會丟棄flutter.jar 中的 /assets/flutter_shared/icudtl.dat檔案到AAR中,導致執行時找不到這個檔案崩潰,在2.+版本中發現沒有這個問題,所以需要使用Android Gradle Plugin 2.+版本,我這邊測試2.2.3版本是ok的。但是Android Gradle 2的版本有一個由來已久的問題就是Library不能獲取project一致 的BuildType,Library預設只發布Release的AAR。這是因為Android中預設指定了釋出type:
private String defaultPublishConfig = "release";
private boolean publishNonDefault = false;
複製程式碼
預設Release,而flutter.gradle中通過buildtype來確定flutter的buildmode,在Android Gradle Plugin 3.+版本中,這個buildtype的問題已經得到解決,這也可能是flutter選用3.+版本的一個原因。
如果避免2.+的buildtype問題呢,網上是有一些獲取project的buildtype配置給Library的方案,比如如何讓library的buildType型別跟app的buildType型別一致(自由定義library的buildType) ??。 我的實現方案是擯棄通過buildtype確定flutter的buildmode的方案,通過直接讀取本地local.properties中的引數來決定,這樣需要自己在本地手動的進行mode的切換,尤其是要注意上線的時候修改為Release模式。不過debug模式下頁面有明顯的debug標識,所以一般也不會出錯。將flutter.gradle 中原有的buildmodefor方法:
private static String buildModeFor(buildType) {
if (buildType.name == "profile") {
return "profile"
} else if (buildType.name == "dynamicProfile") {
return "dynamicProfile"
} else if (buildType.name == "dynamicRelease") {
return "dynamicRelease"
} else if (buildType.debuggable) {
return "debug"
}
return "release"
}
複製程式碼
修改為:
private String buildModeFor(Project project) {
return resolveProperty(project, `flutter.buildMode`, `release`)
}
複製程式碼
這樣在local.properties 中就可以進行debug和Release的切換:
flutter.buildMode=release
flutter.buildMode=debug
切換mode的崩潰問題
在第一次配置好工程或者切換mode的過程中,可能會遇到以下的崩潰問題:
Check failed: vm. Must be able to initialize the VM.
主要是由於不同模式下的產物沒有清理使用了快取,解決辦法是刪除掉所有build檔案的內容再全量編譯一次就可以了。
總結
這是我在做flutter工程配置中遇到的一些坑,文風偏流水賬,請大家見諒,只希望能對大家有一些借鑑意義。另外,我們已經在專案的兩個模組中使用了flutter,開發效率確實能有很大提高,畢竟兩端只需要一個人開發就ok,而且UI小姐姐要求的頁面效果都能不折不扣的完成,上線之後目前還沒發現什麼問題。下一步需要做的是建立一個有效的監控體系,畢竟靠使用者反饋還是不可靠也是滯後的。相信Flutter的未來一定是光明的!