Android 多CPU架構(gòu)支持所需要了解的知識(shí)
Android系統(tǒng)目前支持以下七種不同的CPU架構(gòu):ARMv5详恼,ARMv7 (從2010年起)鸯檬,x86 (從2011年起)廉羔,MipS (從2012年起)匾荆,ARMv8旺订,MIPS64和x86_64 (從2014年起),每一種都關(guān)聯(lián)著一個(gè)相應(yīng)的ABI。ABI是指應(yīng)用基于哪種指令集來(lái)進(jìn)行編譯。 如果項(xiàng)目中使用到了NDK帖汞,它將會(huì)生成.so文件,Android應(yīng)用支持的ABI取決于APK中位于lib/ABI目錄中的.so文件凑术,其中ABI可能是上面說(shuō)過(guò)的七種ABI中的一種翩蘸。 Android包管理器安裝APK時(shí),如果在對(duì)應(yīng)的lib/ABI目錄中存在.so文件的話淮逊,會(huì)自動(dòng)選擇APK包中為對(duì)應(yīng)系統(tǒng)ABI預(yù)編譯好的.so文件催首。當(dāng)一個(gè)應(yīng)用安裝在設(shè)備上,只有該設(shè)備支持的CPU架構(gòu)對(duì)應(yīng)的.so文件會(huì)被安裝泄鹏。在x86設(shè)備上郎任,libs/x86目錄中如果存在.so文件的 話,會(huì)被安裝备籽,如果不存在舶治,則會(huì)選擇armeabi-v7a中的.so文件,如果也不存在,則選擇armeabi目錄中的.so文件(因?yàn)閤86設(shè)備也支 持armeabi-v7a和armeabi)霉猛。
問(wèn)題
如果我們平時(shí)開發(fā)過(guò)程中沒(méi)有很好注意這些尺锚,那么就會(huì)帶來(lái)一些兼容性的問(wèn)題。 比如:你的App支持armeabi-v7a
和x86
架構(gòu)惜浅,然后使用 Android Studio 新增了一個(gè)函數(shù)庫(kù)依賴瘫辩,這個(gè)函數(shù)庫(kù)包含.so文件并支持更多的CPU架構(gòu),那么在某些進(jìn)行上可能會(huì)發(fā)生Crash坛悉,會(huì)出現(xiàn)"UnsatisfiedLinkError"
杭朱,"dlopen: failed"
等等問(wèn)題:
AndroidRuntime:
.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
這是因?yàn)椋闾砑拥囊恍?kù)可能里面做了更多的架構(gòu)適配吹散,因?yàn)橹灰霈F(xiàn)了這個(gè)目錄,系統(tǒng)就只會(huì)在這個(gè)目錄里找.so文件而不會(huì)遍歷其他的目錄八酒,所以就出現(xiàn)了找不到so文件的情況(因?yàn)槠渌夸洓](méi)有這個(gè)so文件)空民。 舉個(gè)例子來(lái)詳細(xì)說(shuō)明一下:我們的APP只支持armeabi-v7a和x86架構(gòu),然后我們的APP使用了一個(gè)第三方的Library羞迷,而這個(gè)Library提供了AMR64等更多類型CPU架構(gòu)的支持界轩,構(gòu)建APK的時(shí)候,這些ARM64的SO庫(kù)依然會(huì)被打包進(jìn)APK里面衔瓮,也就是說(shuō)我們自己的SO庫(kù)沒(méi)有對(duì)應(yīng)的ARM64的SO庫(kù)浊猾,而第三方的Library卻有。這時(shí)候热鞍,某些ARM64的設(shè)備安裝該APK的時(shí)候葫慎,發(fā)現(xiàn)我們的APK里帶有ARM64的SO庫(kù),會(huì)誤以為我們的APP已經(jīng)做好了AMR64的適配工作薇宠,所以只會(huì)選擇安裝APK里面ARM64類型的SO庫(kù)偷办,這樣會(huì)導(dǎo)致我們自己項(xiàng)目的SO庫(kù)沒(méi)有被正確安裝(雖然armeabi-v7a和x86類型的SO庫(kù)確實(shí)存在APK包里面)。
解決辦法
給我們自己的SO庫(kù)也提供AMR64支持澄港,或者不打包第三方Library項(xiàng)目的ARM64的SO庫(kù)椒涯。 使用第二種方案時(shí),可以把APK里面不需要支持的ABI文件夾給刪除回梧,然后重新打包废岂,而在Android Studio下,則可以通過(guò)以下的構(gòu)建方式指定需要類型的SO庫(kù)狱意。 這個(gè)時(shí)候我們就需要使用:
defaultConfig { ...... ndk { abiFilters "", "" } }
來(lái)解決這些問(wèn)題湖苞。 如果你用的Android Studio
編譯器,那么abiFilters
后面加的應(yīng)該是jniLibs/ABI里面所支持的機(jī)型详囤。(當(dāng)然這個(gè)目錄也可以通過(guò)在build.gradle文件中的設(shè)置jniLibs.srcDir屬性自己指定)袒啼。
總結(jié)
APP多架構(gòu)支持
我們平時(shí)用的so的存放位置: - Android Studio工程放在jniLibs/ABI
目錄中(當(dāng)然也可以通過(guò)在build.gradle文件中的設(shè)置jniLibs.srcDir
屬性自己指定) - Eclipse工程放在libs/ABI
目錄中(這也是ndk-build命令默認(rèn)生成.so文件的目錄) - AAR壓縮包中位于jni/ABI
目錄中(.so文件會(huì)自動(dòng)包含到引用AAR壓縮包的APK中) - 最終這些so會(huì)放在編譯好的APK文件中的lib/ABI
目錄中 所有的x86/x86_64/armeabi-v7a/arm64-v8a設(shè)備都支持armeabi架構(gòu)的.so文件,x86設(shè)備能夠很好的運(yùn)行ARM類型函數(shù)庫(kù),但并不保證100%不發(fā)生crash蚓再,特別是對(duì)舊設(shè)備滑肉。64位設(shè)備(arm64-v8a, x86_64, mips64)能夠運(yùn)行32位的函數(shù)庫(kù),但是以32位模式運(yùn)行摘仅,在64位平臺(tái)上運(yùn)行32位版本的ART和Android組件靶庙,將丟失專為64位優(yōu)化過(guò)的性能(ART,webview娃属,media等等)六荒。 基于這些問(wèn)題,我們應(yīng)該盡可能為每種CPU類型都提供對(duì)應(yīng)的SO庫(kù)矾端。 因此以減少APK包大小為由來(lái)減少架構(gòu)的支持顯然不是一個(gè)好的理由掏击,因?yàn)橐部梢赃x擇在應(yīng)用市場(chǎng)上傳指定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
oject.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9] android.
Variants.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架構(gòu)的SO
在android NDK項(xiàng)目的根目錄下面有一個(gè)libs文件夾秩铆,這個(gè)文件夾下面有下面七個(gè)文件夾中的一個(gè)或者多個(gè):arm64-v8a砚亭,armeabi,armeabi-v7a殴玛,mips捅膘,mips64,x86滚粟,x86_64寻仗。 默認(rèn)情況下,NDK的編譯系統(tǒng)根據(jù) “armeabi” ABI生成機(jī)器代碼凡壤∈鹩龋可以使用APP_ABI 來(lái)選擇一個(gè)不同的ABI,我們可以通過(guò)下面的配置來(lái)制定支持的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