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