前言
Android系統目前支持以下七種不同的CPU架構:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯着一個相應的ABI。ABI是指應用基於哪種指令集來進行編譯。
如果項目中使用到了NDK,它將會生成.so文件,Android應用支持的ABI取決於APK中位於lib/ABI目錄中的.so文件,其中ABI可能是上面說過的七種ABI中的一種。 Android包管理器安裝APK時,如果在對應的lib/ABI目錄中存在.so文件的話,會自動選擇APK包中爲對應系統ABI預編譯好的.so文件。當一個應用安裝在設備上,只有該設備支持的CPU架構對應的.so文件會被安裝。在x86設備上,libs/x86目錄中如果存在.so文件的 話,會被安裝,如果不存在,則會選擇armeabi-v7a中的.so文件,如果也不存在,則選擇armeabi目錄中的.so文件(因爲x86設備也支 持armeabi-v7a和armeabi)。
問題
如果我們平時開發過程中沒有很好注意這些,那麼就會帶來一些兼容性的問題。
比如:你的App支持armeabi-v7a
和x86
架構,然後使用 Android Studio 新增了一個函數庫依賴,這個函數庫包含.so文件並支持更多的CPU架構,那麼在某些進行上可能會發生Crash,會出現"UnsatisfiedLinkError"
,"dlopen: failed"
等等問題:
AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[***********] couldn't find "lib*******.so"
或者
AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: "*********.so" is 32-bit instead of 64-bit
這是因爲,你添加的一些庫可能裏面做了更多的架構適配,因爲只要出現了這個目錄,系統就只會在這個目錄裏找.so文件而不會遍歷其他的目錄,所以就出現了找不到so文件的情況(因爲其他目錄沒有這個so文件)。
舉個例子來詳細說明一下:我們的APP只支持armeabi-v7a和x86架構,然後我們的APP使用了一個第三方的Library,而這個Library提供了AMR64等更多類型CPU架構的支持,構建APK的時候,這些ARM64的SO庫依然會被打包進APK裏面,也就是說我們自己的SO庫沒有對應的ARM64的SO庫,而第三方的Library卻有。這時候,某些ARM64的設備安裝該APK的時候,發現我們的APK裏帶有ARM64的SO庫,會誤以爲我們的APP已經做好了AMR64的適配工作,所以只會選擇安裝APK裏面ARM64類型的SO庫,這樣會導致我們自己項目的SO庫沒有被正確安裝(雖然armeabi-v7a和x86類型的SO庫確實存在APK包裏面)。
解決辦法
給我們自己的SO庫也提供AMR64支持,或者不打包第三方Library項目的ARM64的SO庫。
使用第二種方案時,可以把APK裏面不需要支持的ABI文件夾給刪除,然後重新打包,而在Android Studio下,則可以通過以下的構建方式指定需要類型的SO庫。
這個時候我們就需要使用:
defaultConfig {
......
ndk {
abiFilters "***", "***"
}
}
來解決這些問題。
如果你用的Android Studio
編譯器,那麼abiFilters
後面加的應該是jniLibs/ABI裏面所支持的機型。(當然這個目錄也可以通過在build.gradle文件中的設置jniLibs.srcDir屬性自己指定)。
總結
APP多架構支持
我們平時用的so的存放位置:
- Android Studio工程放在
jniLibs/ABI
目錄中(當然也可以通過在build.gradle文件中的設置jniLibs.srcDir
屬性自己指定) - Eclipse工程放在
libs/ABI
目錄中(這也是ndk-build命令默認生成.so文件的目錄) - AAR壓縮包中位於
jni/ABI
目錄中(.so文件會自動包含到引用AAR壓縮包的APK中) - 最終這些so會放在編譯好的APK文件中的
lib/ABI
目錄中
所有的x86/x86_64/armeabi-v7a/arm64-v8a設備都支持armeabi架構的.so文件,x86設備能夠很好的運行ARM類型函數庫,但並不保證100%不發生crash,特別是對舊設備。64位設備(arm64-v8a, x86_64, mips64)能夠運行32位的函數庫,但是以32位模式運行,在64位平臺上運行32位版本的ART和Android組件,將丟失專爲64位優化過的性能(ART,webview,media等等)。
基於這些問題,我們應該儘可能爲每種CPU類型都提供對應的SO庫。
因此以減少APK包大小爲由來減少架構的支持顯然不是一個好的理由,因爲也可以選擇在應用市場上傳指定ABI版本的APK,生成不同ABI版本的APK可以在build.gradle中如下配置:
android {
...
splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
universalApk true //generate an additional APK that contains all the ABIs
}
}
// map for the version code
project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
}
}
}
NDK生成支持多種CPU架構的SO
在android NDK項目的根目錄下面有一個libs文件夾,這個文件夾下面有下面七個文件夾中的一個或者多個:arm64-v8a,armeabi,armeabi-v7a,mips,mips64,x86,x86_64。
默認情況下,NDK的編譯系統根據 “armeabi” ABI生成機器代碼。可以使用APP_ABI 來選擇一個不同的ABI,我們可以通過下面的配置來制定支持的ABI:
TARGET_CPU_API := all
APP_ABI := all
或者是
TARGET_CPU_API := armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64
APP_ABI := armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64