文章

SO和CPU架构

SO和CPU架构

.so 文件,ABI 和 CPU 架构

CPU 架构

  • ARMv5
    早期
  • ARMv7
    2010 年起
  • X86
    2011 年起
  • MIPS
    2012 年起
  • ARMv8
    2014 年起
  • MIPS64
    2014 年起
  • X86_64
    2014 年起

在 Android 系统,每一个 CPU 架构对应一个 ABI,都定义了一种 ABI,ABI 决定了二进制文件如何与系统进行交互:
armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

ABI(Application Binary Interface)

应用程序二进制接口 ABI,定义了二进制文件 (尤其是.so 文件) 如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。

ABI 和 CPU 架构关系

  • 很多设备都支持多余一种 ABI
  • 当一个应用安装在设备上,只有该设备支持的 CPU 架构对应的.so 文件会被安装

最好是针对特定平台提供相应平台的二进制包,这种情况下运行时就少了一个模拟层(例如 x86 设备上模拟 arm 的虚拟层),从而得到更好的性能(归功于最近的架构更新,例如硬件 fpu,更多的寄存器,更好的向量化等)。
我们可以通过 Build.SUPPORTED_ABIS 得到根据偏好排序的设备支持的 ABI 列表。但你不应该从你的应用程序中读取它,因为 Android 包管理器安装 APK 时,会自动选择 APK 包中为对应系统 ABI 预编译好的.so 文件,如果在对应的 src/main/jniLibs 目录中存在.so 文件的话。

CPU 架构支持的(纵向)ABI(横向)armeabiarmeabi-v7aarm64-v8amipsmips64x86x86_64
ARMv5支持      
ARMv7支持支持     
ARMv8支持支持支持    
MIPS   支持   
MIPS64   支持支持  
x86支持支持   支持 
x86_64支持    支持支持

解析:

  • x86 设备上,libs/x86 目录中如果存在.so 文件的话,会被安装,如果不存在,则会选择 armeabi-v7a 中的.so 文件,如果也不存在,则选择 armeabi 目录中的.so 文件。
  • x86 设备能够很好的运行 ARM 类型函数库,但并不保证 100% 不发生 crash,特别是对旧设备。
  • 64 位设备(arm64-v8a, x86_64, mips64)能够运行 32 位的函数库,但是以 32 位模式运行,在 64 位平台上运行 32 位版本的 ART 和 Android 组件,将丢失专为 64 位优化过的性能(ART,webview,media 等等)。
  • armeabi 的 SO 文件基本上可以说是万金油,它能运行在除了 mips 和 mips64 的设备上,但在非 armeabi 设备上运行性能还是有所损耗;
  • 64 位的 CPU 架构总能向下兼容其对应的 32 位指令集,如:x86_64 兼容 X86,arm64-v8a 兼容 armeabi-v7a,mips64 兼容 mips;

参考

关于 Android 的.so 文件你所需要知道的
http://www.jianshu.com/p/cb05698a1968
与 .so 有关的一个长年大坑
https://zhuanlan.zhihu.com/p/21359984

so 的问题

百度地图,三星 s7 加载 so 库失败 log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
09-26 15:40:15.184 21661-21661/com.useus.cs E/NativeLoader: loadException
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.useus.cs/files/libs/libBaiduMapSDK_base_v4_0_0.so" is 32-bit instead of 64-bit
    at java.lang.Runtime.load(Runtime.java:332)
    at java.lang.System.load(System.java:1069)
    at com.baidu.platform.comapi.NativeLoader.f(Unknown Source)
    at com.baidu.platform.comapi.NativeLoader.a(Unknown Source)
    at com.baidu.platform.comapi.NativeLoader.c(Unknown Source)
    at com.baidu.platform.comapi.NativeLoader.loadCustomizeNativeLibrary(Unknown Source)
    at com.baidu.platform.comapi.NativeLoader.loadLibrary(Unknown Source)
    at com.baidu.platform.comapi.a.<clinit>(Unknown Source)
    at com.baidu.platform.comapi.c.a(Unknown Source)
    at com.baidu.mapapi.SDKInitializer.initialize(Unknown Source)
    at com.baidu.mapapi.SDKInitializer.initialize(Unknown Source)
    at com.useus.cs.base.BaseApplication.initBaiduMap(BaseApplication.java:129)
    at com.useus.cs.base.BaseApplication.init(BaseApplication.java:69)
    at com.useus.cs.base.BaseApplication.onCreate(BaseApplication.java:53)
    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1036)
    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6324)
    at android.app.ActivityThread.access$1800(ActivityThread.java:221)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1860)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7232)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
09-26 15:40:15.184 21661-21661/com.useus.cs E/NativeLoader: BaiduMapSDK_base_v4_0_0 Failed to load.
09-26 15:40:15.184 21661-21661/com.useus.cs E/art: No implementation found for int com.baidu.platform.comjni.engine.JNIEngine.initClass(java.lang.Object, int) (tried Java_com_baidu_platform_comjni_engine_JNIEngine_initClass and Java_com_baidu_platform_comjni_engine_JNIEngine_initClass__Ljava_lang_Object_2I)

分析

ABI 兼容的选择。因为如果全部都适配的话,包很大,这样兼容那些用户数极少的 cpu 就很不划算,所以我只适配了 armeabiarmeabi-v7a 以及 x86。但是在三星 S7(ARMv8 架构 CPU) 应该会向下兼容的,但是提示缺报错。由于引入了阿里百川的兼容包 feedbackSdk.aar,里面有各个平台的.so 文件,也就是阿里百川做了各个平台的兼容,所以它创建了各个兼容的平台目录,因为只要出现了这个目录,系统就只会在这个目录里找.so 文件而不会遍历其他的目录,所以就出现了之前找不到.so 文件的情况(因为其他目录没有我的.so 文件)。

解决

  • app/build.gradle 文件中进行 abi 的过滤
1
2
3
4
5
6
7
8
9
10
android {
    // ...
    defaultConfig {
        // 过滤支持的abis
        ndk {
            abiFilters 'armeabi', 'armeabi-v7a', 'x86'
        }
    }
    // ...
}
  • 编译时会报错,在 gradle.properties 添加下面代码
1
android.useDeprecatedNdk=true

错误见:

1
2
Error:(36, 1) A problem occurred evaluating project ':app'.
> Error: NDK integration is deprecated in the current plugin.  Consider trying the new experimental plugin.  For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental.  Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.

参考

本文由作者按照 CC BY 4.0 进行授权