Demo例子代码:https://github.com/sayhellotogithub/AbifiltersAndSplit
Android支持多种CPU处理器架构:
- mips
- mips64
- armeabi
- armeabi-v7a
- arm64-v8a
- x86
- x86_64
想要在项目中使用 native 类库,我们必须对要支持的处理机框架提供对应编译包。每个处理器架构需要我们提供一个或多个包含native代码的.so文件。
当我们决定支持处理器架构的时候,相应的APK会疯狂的增大。对于用户来说设备架构只需要一个子集,但当用户下载APK时,会全部下载(对用户来说相当的不好)。
通过Android Studio 查看APK文件,可以发现lib文件夹占用APK空间比较大:
进一步看下lib文件夹下的文件,可以清楚的看到不同处理器架构文件的native库的大小:
当前 Google Play Store 上传APK限制是100MB。而我们的native库占用APK应用一半以上的空间。为了减少APK的大小,我们需要限制支持的处理器架构。
在这里我介绍两种技术:
- ABI Filters
- APK Split
ABI Filters
ABI (Application Binary Interface)是两个程序模块之间的接口; 通常,其中一个是库文件或者是操作系统
ABI filters 可以让我们包含进APK里处理器架构native文件。
在defaultConfig中加入如下配制:
ndk {
abiFilters "arm64-v8a", "armeabi-v7a"
}
通过指定处理器的架构,我们可以看到我们的包小了很多:
通过abiFilters配制有利有弊,在这里以用户角度与开发者角角来分析下。
用户角度:APK包含了用户用不到的类库,造成APK变大,用户需要花更多的网络流量及下载的等待时间;
开发者角度:这种方案提供了单一的APK,节省开发者的维护成本。
如果我们考虑到包大小超过100M或者用户角度的话,ABI filters不再是一个可选方案。我们需要确保用户下载的只有用户需要的native库。这时我们需要使用APK split 技术。
APK split
APK split 允许我们自动生成多个APK文件。我们可以通过屏幕密度(mdpi, hdpi, xhdpi…)或者处理器架构(arm64-v8a, armeabi-v7a…)来进行拆分。
通过处理架构配制:
splits{
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86, armeabi-v7a, and mips.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
// Specifies that we want to also generate a universal APK that includes all ABIs.
universalApk true
}
}
生成Debug包:
但是由于Debug包拆分不是必须的,我们可以配制仅对release包用效。
splits {
abi {
def isReleaseBuild = false
gradle.startParameter.taskNames.find {
// Enable split for release builds in different build flavors
// (assemblePaidRelease, assembleFreeRelease, etc.).
if (it ==~ /:app:assemble.*Release/) {
isReleaseBuild = true
return true // break
}
return false // continue
}
// Enables building multiple APKs per ABI.
enable isReleaseBuild
universalApk true
}
}
运行项目之后:
Version codes
由于应用商店不允许上传具有相同的VersionCode的多个APK包。我们需要对每个Release包生成对应的VersionCode。
// Map for the version code that gives each ABI a value.
def abiCodes = ['armeabi-v7a':1, 'arm64-v8a':2,'x86':3, 'x86_64':4]
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.each {
output ->
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) * 100000 + variant.versionCode
}
}
我们通过Android Studio 查看app-arm64-v8a-release APK文件,发现versionCode变成了20001:
需要支持的处理器架构
由于处理器架构为armeabi-v7a、arm64-v8a占市场的99%以上的份额,因此我们必须要支持。
这里有一份Android 处理器架构的汇总建议:
- mips (已弃用)
- mips64 (已弃用)
- armeabi (已弃用)
- armeabi-v7a (需要支持— 现在最流行的处理器架构)
- arm64-v8a (需要支持 — armeabi-v7a的新版本)
- x86 (可选, 设备非常有限,可以用于模拟器debugging)
- x86_64 (可选, 设备非常有限,可以用于模拟器debugging)
相关的参考
https://developer.android.com/google/play/publishing/multiple-apks.html#HowItWorks
https://proandroiddev.com/reducing-apk-size-by-using-abi-filters-and-apk-split-74a68a885f4e