学而思网校1对1的android工程是用flutter框架实现的,而android工程不可避免的要和cpu架构和so库打交道,在实践中,针对flutter框架对cpu架构的支持,我们也有一些经验在此总结。
so库说明
so库在android中,是使用c/c++代码编译出来的库文件,可以使用ndk调用,就是你在android代码中见到的native方法,具体的实现就在so库中。
关于so库兼容性问题
andorid中或多或少都会引用到第三方库,而很多第三方库中都有so的存在,不论是复制到项目中(如百度地图),或是gradle依赖(如个推)
其中都涉及到了so库的相关问题,如果你选择的库是有所有cpu类型可选还好,如果不是,那么就需要自定义设置了。
举个栗子
你的app依赖两个库,分别是lib1,lib2
lib1: arm64-v8a,armeabi-v7a
lib2: armeabi-v7a
那么当你运行在v7的手机上时,因为你的项目含有v7的so库,所以没有问题,可以跑起来如果,你运行在v8手机上,那么你的项目就会Crash, 为啥呢? 这就涉及到so对齐了
so对齐
简单来说,就是要有就必须都有,如果一个没有,那就一个都不要
比如上面的例子,如果你是自己复制到项目下的,你需要删掉arm64-v8a的文件夹
flutter经典问题couldn't find "libflutter.so"
引发的原因
导致这个问题出现的原因是因为我们在项目种使用了so库,或者项目中引用的三方sdk使用了so库。在引用so库时需要针对不同的cpu架构使用不同的.so文件。armeabi,armeabi-v7a,x86,arm64-v8a,大家通常会对这几个cpu架构进行适配。然而问题就出在现在Flutter在打包Apk时不能同引入arm32和arm64的libflutter.so。我门将打好包的Apk安装到arm64架构(默认打包会引入arm32)的手机上就出现了couldn't find libflutter.so这个异常。
如何解决
针对arm32和arm64分别打包(flutter 提供了命令来之分别对arm32和arm64分别打包)
flutter build apk --target-platform=android-arm32
flutter build apk --target-platform=android-arm64
我们用flutter build apk --target-platform=android-arm64打包apk,并在arm64cpu架构的手机安装运行,很好完美运行。但是当我们吧apk安装到32位时问题再次出现,原因就不再重复了。很显然到这里这个方式并不能解决这个问题,为了适配arm32和arm64我们需要分别打包,而国内大部分应用市场不能针对不同cpu架构上传不同的apk。
不对arm64做适配,打包时排除其他非arm32架构的so文件
这时候有些朋友可能会又疑问,问题不就是因为打包时没有引入arm64的libflutter的so文件导致在arm64架构手机上出现“兼容”问题的么。arm64cpu架构是可以像下兼容的,简单点就是arm64架构的cpu可以使用arm32的.so文件。出现问题真正原因是我们在引用so库时(引用的三方库中引用了so库),对arm64做了兼容。这样就会导致运行时系统误以为我们的应用对所有的so库做了arm64架构的兼容,但是在打包时libflutter并没有引入arm64的版本。这就导致系统去寻找arm64版本的libflutter发现找不到。系统误会了我们,我们只能通过gradle在打包时排除其他非arm32架构的so文件来解除这个误会。
好那如何排除其他非arm32架构的so文件。在app下的gradle文件加入如下代码
buildTypes {
release {
ndk{
//这里其实我觉可以直接是用"armeabi-v7a",但国内几个大哥之前使用的都是"armeabi"
abiFilters "armeabi"
}
}
debug {
ndk {
//这里要加上,否则debug包会出问题,后面三个为可选,x86建议加上不然部分模拟器回报错
abiFilters "armeabi", ,"armeabi-v7a","arm64-v8a", "x86"
}
}
}
3.flutter 1.7.8+hotfix.2 以上版本已经支持flutter 64位了 直接打包即可.
只支持64位:flutter build apk --target-platform android-arm64
支持32位:flutter build apk --target-platform=android-arm
同时支持64位和32位:flutter build apk
工程加入x5内核引起的新问题
经过上述文章方法的处理,我们的工程解决了在64位手机上,出现couldn't find "libflutter.so"引起的Crash问题,但是,当我们引入x5内核后,又出现了新的问题。
这是我们在没有引入x5内核前工程支持的cpu架构配置,这个地方需要说明,如果不加入 "arm64-v8a"配置的话,flutter build apk命令虽然会将so库打入工程,但是平时开发时使用的flutter run apk命令并不会把so库打入工程。
ndk {
abiFilters "armeabi" ,"armeabi-v7a", "arm64-v8a"
}
这种配置方式能保证flutter run和flutter build同时生效,不会引起程序因为找不到so库引起的Crash。
但是当引入x5内核后,会出现如下错误
X5 does not support the 64-bit mode to run
x5官方解释是x5内核暂时不提供64位的so文件,在64位手机上需要让AP以32位模式运行,所以我们如果加入了 "arm64-v8a"的话,x5内核在64位手机无法启动。
但是加入的话,又会引起flutter的so库找不见,解决办法如下:
修改flutter.gradle里面,关于flutter run的配置,使其支持把64位的so库打入工程
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();
if (useLocalEngine(project)) {
String engineOutPath = project.property('localEngineOut')
File engineOut = project.file(engineOutPath)
if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build')
}
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException("Local engine jar not found: $flutterJar")
}
localEngine = engineOut.name
localEngineSrcPath = engineOut.parentFile.parent
// The local engine is built for one of the build type.
// However, we use the same engine for each of the build types.
project.android.buildTypes.each {
addApiDependencies(project, it.name, project.files {
flutterJar
})
}
}
修改构建脚本,在jenkens上构建工程时,动态修改cpu架构支持
#andorid app build.gradle 工程配置文件地址
# 删除 arm64-v8a,防止 hybrid 在某些手机上无法构建。
androidAppGradlePath="$JENKINS_WORKSPACE/northstar_flutter/android/app/build.gradle"
echo "App 中 app/build.gradle 配置文件地址是:${androidAppGradlePath}"
sed -i 's/, "arm64-v8a"/ /' $androidAppGradlePath
echo "已经将 arm64-v8a 从文件中剔除,文件路径是:$androidAppGradlePath"
我们最终采用了方案2,因为每次更新flutter的版本,flutter.gradle文件都会被官方修改,尽量不动此文件为好。
总结
以上就是我们在学而思网校1对1android工程中对flutter框架支持so库做的一些实践和总结,随着flutter版本的不断更新,相信官方会对cpu架构支持越来越好。