早期的 Android 系统差点儿仅仅支持 ARMv5 的 CPU 架构,你知道如今它支持多少种吗?7 种。
Android 系统眼下支持以下七种不同的 CPU 架构:ARMv5。ARMv7 (从 2010 年起),x86 (从 2011 年起),MIPS (从 2012 年起),ARMv8,MIPS64 和 x86_64 (从 2014 年起),每一种都关联着一个相应的 ABI。
应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是. so 文件)怎样执行在相应的系统平台上。从使用的指令集,内存对齐到可用的系统函数库。
在 Android 系统上。每一个 CPU 架构相应一个 ABI:armeabi,armeabi-v7a,x86。mips,arm64-v8a,mips64,x86_64。
假设项目中使用到了 NDK,它将会生成. so 文件,因此显然你已经在关注它了。假设仅仅是使用 Java 语言进行编码,你可能在想不须要关注. so 文件了吧,由于 Java 是跨平台的。
但其实,即使你在项目中仅仅是使用 Java 语言,非常多情况下,你可能并没有意识到项目中依赖的函数库或者引擎库里面已经嵌入了. so 文件,并依赖于不同的 ABI。
比如,项目中使用 RenderScript 支持库,OpenCV。Unity,android-gif-drawable,SQLCipher 等,你都已经在生成的 APK 文件里包括. so 文件了。而你须要关注. so 文件。
Android 应用支持的 ABI 取决于 APK 中位于 lib/ABI 文件夹中的. so 文件。当中 ABI 可能是上面说过的七种 ABI 中的一种。
Native Libs Monitor 这个应用能够帮助我们理解手机上安装的 APK 用到了哪些. so 文件,以及. so 文件来源于哪些函数库或者框架。
当然。我们也能够自己对 app 反编译来获取这些信息,只是相对麻烦一些。
非常多设备都支持多于一种的 ABI。比如 ARM64 和 x86 设备也能够同一时候执行 armeabi-v7a 和 armeabi 的二进制包。
但最好是针对特定平台提供相应平台的二进制包。这样的情况下执行时就少了一个模拟层(比如 x86 设备上模拟 arm 的虚拟层),从而得到更好的性能(归功于近期的架构更新,比如硬件 fpu。很多其它的寄存器,更好的向量化等)。
我们能够通过 Build.SUPPORTED_ABIS 得到依据偏好排序的设备支持的 ABI 列表。但你不应该从你的应用程序中读取它,由于 Android 包管理器安装 APK 时,会自己主动选择 APK 包中为相应系统 ABI 预编译好的. so 文件,假设在相应的 lib/ABI 文件夹中存在. so 文件的话。
处理. so 文件时有一条简单却并不知名的重要法则。
你应该尽可能的提供专为每一个 ABI 优化过的. so 文件,但要么全部支持。要么都不支持:你不应该混合着使用。你应该为每一个 ABI 文件夹提供相应的. so 文件。
当一个应用安装在设备上。仅仅有该设备支持的 CPU 架构相应的. so 文件会被安装。
在 x86 设备上,libs/x86 文件夹中假设存在. so 文件的话。会被安装,假设不存在,则会选择 armeabi-v7a 中的. so 文件。假设也不存在,则选择 armeabi 文件夹中的. so 文件(由于 x86 设备也支持 armeabi-v7a 和 armeabi)。
当你引入一个. so 文件时,不止影响到 CPU 架构。我从其它开发人员那里能够看到一系列常见的错误,当中最多的是 "UnsatisfiedLinkError","dlopen: failed" 以及其它类型的 crash 或者低下的性能:
使用 NDK 时。你可能会倾向于使用最新的编译平台,但其实这是错误的,由于 NDK 平台不是后向兼容的,而是前向兼容的。推荐使用 app 的 minSdkVersion 相应的编译平台。
这也意味着当你引入一个预编译好的. so 文件时,你须要检查它被编译所用的平台版本号。
.so 文件能够依赖于不同的 C++ 执行时,静态编译或者动态载入。混合使用不同版本号的 C++ 执行时可能导致非常多奇怪的 crash,是应该避免的。
作为一个经验法则。当仅仅有一个. so 文件时,静态编译 C++ 执行时是没问题的,否则当存在多个. so 文件时。应该让全部的. so 文件都动态链接同样的 C++ 执行时。
这意味着当引入一个新的预编译. so 文件,并且项目中还存在其它的. so 文件时。我们须要首先确认新引入的. so 文件使用的 C++ 执行时是否和已经存在的. so 文件一致。
这一点在前文已经说到了,但你应该真的特别注意它,由于它可能发生在根本没有意识到的情况下。
比如:你的 app 支持 armeabi-v7a 和 x86 架构。然后使用 Android Studio 新增了一个函数库依赖,这个函数库包括. so 文件并支持很多其它的 CPU 架构,比如新增 android-gif-drawable 函数库:
- compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.+'
公布我们的 app 后,会发现它在某些设备上会发生 Crash,比如 Galaxy S6,终于能够发现仅仅有 64 位文件夹下的. so 文件被安装进手机。
解决方式:又一次编译我们的. so 文件使其支持缺失的 ABIs,或者设置
- ndk.abiFilters
显示指定支持的 ABIs。
最后一点: 假设你是一个 SDK 提供者,但提供的函数库不支持全部的 ABIs。那你将会搞砸你的用户,由于他们能支持的 ABIs 必将仅仅能少于你提供的。
我们往往非常 easy 对. so 文件应该放在或者生成到哪里感到困惑,以下是一个总结:
全部的 x86/x86_64/armeabi-v7a/arm64-v8a 设备都支持 armeabi 架构的. so 文件。因此似乎移除其它 ABIs 的. so 文件是一个降低 APK 大小的好技巧。但其实并非:这不仅仅影响到函数库的性能和兼容性。
x86 设备能够非常好的执行 ARM 类型函数库。但并不保证 100% 不发生 crash,特别是对旧设备。64 位设备(arm64-v8a, x86_64, mips64)能够执行 32 位的函数库,可是以 32 位模式执行,在 64 位平台上执行 32 位版本号的 ART 和 Android 组件。将丢失专为 64 位优化过的性能(ART。webview,media 等等)。
以降低 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
- }
- }
- }
来源: http://www.bubuko.com/infodetail-2117591.html