Android中的ABI
-
ABI是Application Binary Interface的缩写。
-
ABI常表示两个程序模块之间的接口,且其中一个模块常为机器码级别的library或操作系统。
-
ABI定义了函数库的调用、应用的二进制文件(尤其是.so)如何运行在相应的系统平台上等细节。
-
Android目前支持以下七种ABI:armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64。
Android中的CPU
-
不同的Android设备使用支持不同指令集的CPU。
-
Android目前有以下七种cpu架构:ARMv5、ARMv7、ARMv8、x86、x86_64、MIPS和MIPS64。
Android中的ABI与CPU
- 每种CPU架构都有其自己支持的ABIs。可通过
Build.SUPPORTED_ABIS
得到根据偏好排序的设备支持的ABI列表。
CPU(纵向)/ABI(横向) | armeabi | armeabi-v7a | arm64-v8a | x86 | x86_64 | mips | mips64 |
---|---|---|---|---|---|---|---|
ARMv5 | 支持(1) | ||||||
ARMv7 | 支持(2) | 支持(1) | |||||
ARMv8 | 支持(3) | 支持(2) | 支持(1) | ||||
x86 | 支持(3) | 支持(2) | 支持(1) | ||||
x86_64 | 支持(4) | 支持(3) | 支持(2) | 支持(1) | |||
MIPS | 支持(1) | ||||||
MIPS64 | 支持(2) | 支持(1) |
表中的数字表示最终选择安装的ABI的优先级,1为最高。
- 每种CPU架构都有自己对应的最优ABI。
CPU架构 | 最优ABI |
---|---|
ARMv5 | armeabi |
ARMv7 | armeabi-v7a |
ARMv8 | arm64-v8a |
x86 | x86 |
x86_64 | x86_64 |
MIPS | mips |
MIPS64 | mips64 |
-
应用安装到设备时,只有该设备的CPU架构支持的最优so库才会被安装。
如:x86架构的设备支持x86、armeabi-v7a和armeabi等ABI。但优先级从高到低依次为x86、armeabi-v7a、armeabi。系统会根据此顺序寻找首个可用的最优的so库,找到则结束。
-
x86设备包含ARM模拟层,能够很好地运行ARM类型的so库,但并不保证100%不发生Crash。
-
64位设备(arm64-v8a, x86_64, mips64)能够运行32位的so库。但是以32位模式运行时,会丢失专为64位优化过的性能特征(ART, WebView, Media, etc.)。
NDK兼容性
-
NDK平台不是向后兼容(兼容过去的版本)的,而是向前兼容(兼容未来的版本)的。
-
NDK编译的版本应该尽量使用较低的版本,如minSdkVersion="8"。
Android中的so库
so库的名称和文件名
-
so库的名称可任意,如daking。
-
so库的文件名必须以lib开头。如libdaking.so,其中lib是必要前缀,daking才是这个库的名称。
so库文件的路径
-
在Android Studio中的路径:
默认要放在
模块/src/<SourceSet>/jniLibs/<ABI>/
下。如:将so库放在app/src/main/jniLibs/armeabi
。可在模块的
build.gradle
中修改配置指定so库的路径。如,将so库放在app/libs/armeabi
下,并修改配置如下:
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
-
在Eclipse中的路径:
放在
libs/<ABI>
下。 -
在Android系统中的路径:
/data/data/<应用包名>/lib/
下。 -
在aar包中的路径:
jni/<ABI>
下。
so库的代码加载
-
System.loadLibrary("so库的名称");如System.loadLibrary("daking");会加载该应用下的
libdaking.so
文件。 -
System.load("so库文件的全路径");如System.load("/data/data/com.daking.app.demo/libdaking.so");会加载包名为
com.daking.app.demo
的应用的libdaking.so
文件。
so库的使用建议
-
理论上应该为每个ABI目录提供对应的so库。但是Android支持7种ABI,若全部支持,必然导致APK包过大。
-
一般只保留armeabi、armeabi-v7a这两个ABI的so库。
mips/mips64:极少用于手机,可忽略。
x86/x86_64:x86架构的手机的市场占有率很低,约为1%左右。而且x86架构都包含ARM模拟层,兼容ARM类型的ABI。注意,模拟器为x86架构。
arm64-v8a:64位ARM架构。可用32位模式运行armeabi-v7a和armeabi。
armeabi-v7a:截止于2017年2月,目前主流版本AMRv7。
armeabi:老版本ARMv5,但仍需要兼容。
-
通过上面的分析,不难发现,只要提供armeabi便可兼容新/旧设备。但armeabi缺少对浮点数机器的支持,存在性能瓶颈。应该将armeabi目录中的so库拷贝一份到armeabi-v7a目录中。
-
总结:
为了减小apk体积,只保留armeabi和armeabi-v7a两个目录,并保证这两个目录中so库数量一致。
对只提供armeabi版本的第三方so库,原样复制一份到armeabi-v7a目录中。
so库的常见错误
-
使用高版本编译出的so库运行在低版本的平台上会出错。考虑到NDK是向前兼容的,应使用低版本编译。
-
so库放置的路径有误。
-
没有在每个生效的ABI目录下放置对应的so库。
- 若某应用有armeabi和arm64-v8a两个ABI目录,armeabi目录里有
a.so
和b.so
,但arm64-v8a只有a.so
。 - 当ARMv8设备在安装此应用时,根据ABI优先级,首先发现arm64-v8a目录存在,并决定使用此ABI下的so库。
- 但arm64-v8a目录中没有
b.so
,于是报错。 - 此时的解决方案有:一,删除arm64-v8a目录;二,arm64-v8a目录的so库情况要与armeabi一致。
- 若某应用有armeabi和arm64-v8a两个ABI目录,armeabi目录里有
工具
- Native Libs Monitor,监控设备上所有已安装的App,列出它们apk中包含的so库以及检测出最终安装到的so库。
网友评论
比如说一个设备主ABI 是x86_64 ,辅ABI 是 armV7 , 而代码中支持了armeabi 和 x86 ,首先找主ABI x86_64 没有找到,它不会按你说的优先级 找x86, 找的是armeabi ,不相信的可以用小米pad 2验证下
反解大厂的的apk 看下,基本就支持了arm ,为什么呢 ?因为1,主流架构就是arm ,ndk 向前兼容性原则 使用armebai 就足够了,2、一般教育类的app 在平板上的用户量比率会多一些,而平板支持x86的比率也比较多一些,所以一般还要支持x86 , 但为什么 很多app 仍然只支持armeabi 呢 ?主要是因为 上面说的主、辅abi ,一般情况下,主abi 为x86的设备,都有辅abi arm ,所以只要 支持 armeabi 就足够了
但有很特殊的情况,就是有的设备就只支持 x86 ,也就是没有辅abi arm ,如果要兼容这个设备,那就再加一个 x86 就行了,其它加了会增加apk 的体积
关于主abi和辅abi ,官网有介绍,https://developer.android.com/ndk/guides/abis?hl=zh-cn
但一个app 用户量很多的情况下,armeabi 就还需要留下,如果要去掉,必须有相关的官方说明 ,一切用户至上。用户量很少、或者新的app ,可以不用考虑 。