Android 多CPU架构支持所需要了解的知识
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:
.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
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架构的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