Bugtags V1.2.7 引入 NDK SO 库

目录
  1. 1. 原理
  2. 2. NDK SO 支持不同的 CPU 构架
  3. 3. 合并打包到 APK 中
  4. 4. 系统安装 APK
  5. 5. 问题来了
  6. 6. 解决方案
    1. 6.1. 准则
    2. 6.2. 折中方案
      1. 6.2.1. Android Studio
    3. 6.3. Eclipse
  7. 7. 参考文献

为了接下来 Native Crash 的捕获,V1.2.7 引入了 NDK SO 库,如果您的 Android App 原先使用了 NDK SO 库,请参照本文档,按照实际情况来设置输出的 APK 所支持的 ABI CPU 构架。

原理

Android 系统本质是一个经过改造的 Linux 系统。最早,Android 系统只支持 ARMv5 的 CPU 构架,随着 Android 系统的发展,又加入了 ARMv7 (2010), x86 (2011), MIPS (2012), ARMv8, MIPS64 和 x86_64 (2014)。

每一种 CPU 构架,都定义了一种 ABI(Application Binary Interface),ABI 决定了二进制文件如何与系统进行交互。

一般情况下,你不需要关注这些。当你的 APP 中用到了些包含 SO 库第三方库,或者自己使用 NDK 来实现了某些功能,你就需要认真阅读接下来的教程。

NDK SO 支持不同的 CPU 构架

在使用 NDK 开发包含 c/c++ 代码的 SO 库的时候,你可以选择输出支持如下 ABI CPU 构架:

1
2
3
4
5
6
7
armeabi
armeabi­v7a
arm64­v8a
x86
x86_64
mips
mips64

Bugtags 的 NDK 库支持如上所有的 CPU 构架:

bugtags-ndk

但不是所有人的开发者提供的 NDK 库都支持所有的 CPU 构架:

other-ndk

上面的这个开发者提供的库,就只支持 armeabi。

其实一般情况下,是没有问题的,x86 的设备,也会兼容 armeabi 的 SO 库。

合并打包到 APK 中

如果不做任何设置,Android 的构建系统会把这些来自不同开发者的 SO 库都合并在一起,打进 APK 压缩包中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│   └── libBugtags.so
├── res

系统安装 APK

根据官方 ndk-abi 文档, Android 系统在安装一个 APK 的时候,会考虑当前的设备的 CPU 构架和配置(称为所谓的 primary-abi 和 secondary-abi),去该 APK 文件的对应文件夹去寻找 SO 库。

假设当前设备是 x86 机器,会优先去 lib/x86 文件夹下寻找 SO 库:

1
lib/<primary-abi>/lib<name>.so

如果找不到,同时定义了 secondary-abi,则去如下文件夹寻找:

1
lib/<secondary-abi>/lib<name>.so

如果找到了,就将文件拷贝到 APK 的安装目录的如下文件夹中:

1
/lib/lib<name>.so

找不到对应的 SO,安装正常,但是当这个 SO 在运行时被使用时,会崩溃。

问题来了

可能你已经发现问题了,当一个 APK 是这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│   └── libBugtags.so
├── res

同时 APK 被安装到一个 x86 的设备上的时候,以上的寻找过程,将会失败,运行时,将出现如下报错:

1
2
D/xxx   (10674): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx-2/base.apk"],nativeLibraryDirectories=[/data/app/xxx-2/lib/x86, /vendor/lib, /system/lib]]] couldn't find "libirdna_sdk.so"
D/xxx (10674): at java.lang.Runtime.loadLibrary(Runtime.java:366)

此处,笔者有点费解,既然在 x86 文件夹中找不到,应该去 armeabi 文件夹中自动寻找啊,此处留一个 TODO,需要接下来去确认是否是某些机器的原因。

解决方案

准则

NDK SO 开发者应该遵循一个准则:支持所有的平台,否则将会搞砸你的用户。

NDK SO 使用者应该遵循一个准则:要么支持所有平台,要么都不支持。

然而,事与愿违,因为种种原因(遗留 SO、芯片市场占有率、APK 包大小等),并不是所有人都遵循这样的原则。

折中方案

Android Studio

  • Android Gradle 插件中,可以使用如下方式对 abi 进行过滤:
1
2
3
4
5
6
7
8
9
10
11
12
13
android {
...

defaultConfig {
...
ndk {
// 设置支持的 SO 库构架,注意这里要根据你的实际情况来设置
// 一般而言,取你所有的库支持的构架的交集
abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
}
}

}

关键行:

1
abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'

根据你的 APP 中使用的 SO 库所支持的构架具体情况,你可以进行具体设置。最终输出的 apk 中,将会包含你所选择的 abi。

像前面举出的例子,就应该只允许 armeabi。

  • 如果在添加 “abiFilter” 之后 Android Studio 出现以下提示:
1
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin

则在项目根目录的 gradle.properties 文件中添加:

1
android.useDeprecatedNdk=true

Eclipse

Eclipse 中,你需要手动控制你的工程中的这个文件夹里面的内容:

eclipse-libs

以达到上述的原则,使得在不同的构架的设备上运转正常。

具体操作,是将取你所有的 so 库所支持的构架的交集,移除其他。

lib a:

1
2
3
armeabi/a.so
armeabi­v7a/a.so
arm64­v8a/a.so

lib b:

1
2
3
armeabi/b.so
armeabi­v7a/b.so
x86/b.so

交集

1
2
armeabi/a.so
armeabi­v7a/a.so

参考文献

What you should know about .so files

关于Android的.so文件你所需要知道的)

ABI Management

相关文章推荐