把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的未来一定是光明的!