NDK SO 库开发与使用中的 ABI 构架选择

Bugtags V1.2.7 引入了 NDK SO 库,在集成的时候,遇到不同的 SO 库打包到 APK 时,安装在某些机器上,出现 java.lang.UnsatisfiedLinkError 加载失败。

为此,深究了一下原理,和给出了解决方案。

原理

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 构架:

armeabi
armeabi­v7a
arm64­v8a
x86
x86_64
mips
mips64

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

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

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

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

合并打包到 APK 中

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

├── 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 库:

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

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

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

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

 /lib/lib<name>.so

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

问题来了

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

├── 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 的设备上的时候,以上的寻找过程,将会失败,运行时,将出现如下报错:

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 进行过滤:
android {
    ...

    defaultConfig {
        ...
        ndk {
            // 设置支持的 SO 库构架,注意这里要根据你的实际情况来设置
            abiFilters ‘armeabi‘// ‘armeabi-v7a‘, ‘arm64-v8a‘, ‘x86‘, ‘x86_64‘, ‘mips‘, ‘mips64‘
        }
    }

}

关键行:

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

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

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

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

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

    android.useDeprecatedNdk=true

Eclipse

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

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

参考文献

What you should know about .so files

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

ABI Management

?

时间: 2024-10-16 00:23:03

NDK SO 库开发与使用中的 ABI 构架选择的相关文章

iOS开发——装逼技术精选&amp;全面了解 iOS 静态库开发

全面了解 iOS 静态库开发 简介 在企业开发中,一些核心技术或者常用框架,出于安全性和稳定性的考虑,不想被外界知道,所以会把核心代码打包成静态库,只暴露头文件给程序员使用(比如:友盟.百度地图等第三方的sdk) 静态库和动态库的存在形式 静态库:.a 和 .framework 动态库:.dylib 和 .framework 静态库和动态库的区别 静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统

Cocos开发中Visual Studio下libcurl库开发环境设置

我们介绍一下win32中Visual Studio下libcurl库开发环境设置.Cocos2d-x引擎其实已经带有为Win32下访问libcurl库,Cocos2d-x 3.x中libcurl库文件所在位置是<工程目录>\cocos2d\external\curl\prebuilt\win32目录中,头文件在<工程目录>\cocos2d\external\curl\include\win32目录中.首先,我们需要配置头文件搜索路径,选中HelloCpp工程,打开菜单"项

boost标准库开发环境搭建

1.下载boost相关的库的安装包 网址:http://www.boost.org/ 其中1.55.0版本的下载地址是:http://sourceforge.net/projects/boost/files/boost/1.55.0/ 截图: 2.boost开发相关的软件: boost_1_55_0.tar.gz  Linux平台下面的boost源码包 boost_1_55_0.zip    Windows平台下面的boost源码包 boost_1_55_0-bin-msvc-all-32-64

C# 调用其他的动态库开发应注意的问题

1.背景 程序开发语言可以说是五花八门,这就引出了一个新问题 ,不同语言开发的系统进行对接时相关调用的问题. 下面我主要说一下我自己在做接口开发时遇到的问题及解决方法仅供参考,我使用的C#开发进行对接其他程序. 2.具体做法 首先,谈一下目前系统对接的几种常见对接方式.a.通过非托管的动态库dll文件导入.b.通过对方提供的COM组件调用.c.通过webService进行调用. 目前比较常用的就这个几种.下面一起看看都是如何具体调用实现以及遇到的问题. a.通过非托管的动态库dll文件导入 此种

JavaWeb-12 (自定义标签库开发&amp;JSTL标签库)

JavaWeb-12:自定义标签库开发&JSTL标签库 自定义标签库开发 一.自定义标签简介 自定义标签主要用于移除Jsp页面中的java代码. 使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步骤: 1.编写一个实现Tag接口的Java类(标签处理器类). 2.编写标签库描述符(tld)文件,在tld文件中把标 实验:项目架构如下: a.tld <?xml version="1.0" encoding="utf-8" ?> &l

新手上手STM32是学习库开发还是寄存器开发?

有需要资料的可以加我:腾讯QQ3249838614经常会有一些刚接触STM32的人问这个问题,也在其他论坛回答过, 我个人认为,在回答这个问题之前,你得先问清楚自己,我学习stm32.或者再往更深的地方走,我学习单片机,究竟是为了什么?你现在是处于什么状态?你是学生还是已经参加工作了?你的专业或者你的工作是与单片机相关的还是不相关的?你未来是否想从事这个行业或者想跳槽到这个行业? 这些所有的问题的答案都可以把学习单片机的人分为两大类:第一类:学习单片机是为了把这个技术当做一技之长安身立命:第二类

使用固件库开发和使用Hal库开发有什么不同

ST 先后提供了两套固件库:标准库和 HAL 库. STM32 芯片面市之初只提供了丰富全面的 标准库,大大便利了用户程序开发,为广大开发板所推崇,同时也为 ST 积累了大量标准库用 户.有过 STM32 基础的同学想必对标准库非常熟悉.我们正点原子目前的所有 STM32F1 开发 板以及探索者 STM32F407 开发板都是采用的标准库. 目前网络学习资料和源码,绝大多数都 是采用的标准库.       大约到 2014 年左右, ST 在标准库的基础上又推出了 HAL 库. 实际上, HAL

Vue组件库开发

市面上目前已有各种各样的UI组件库,比如 Element 和 iView,他们的强大毋庸置疑.但是我们面临的情况是需求越来越复杂,当它们不能再满足我们需求的时候,这个时候就有必要开发一套属于自己团队的组件库了. 为何要进行组件库开发 如果你所在的公司对于页面的样式没有什么要求,那么你只要随便拿一个组件库来用就行了,比如element.iView等等,不用再重复造轮子了: 如果你目前只有个人用一个组件,或者是只对个别组件有要求,那么只要在你的工程里面开发一个.vue单文件组件就可以了:如果你的团队

现代前端库开发指南系列(一):融入现代前端生态

本系列文章讲什么内容? 本系列文章主要介绍如何在现代前端生态下,创建一个工业级别的库.近几年来,前端工程化.模块化.组件化的大潮铺天盖地而来,在解决以往的架构痛点之余,却又产生了信息过载的问题:我希望通过分享自己的经验,帮助大家少踩坑多出活. 为什么需要开发一个前端库呢? 在项目开发过程中,总有一些功能是相同或类似的,如果你只是单纯地复制粘贴这部分代码,那么恭喜你,假以时日,需求一改,你就只能自尝苦果了. 你说,这还不简单,抽成公共方法公用不就好了吗?对的没错,但请把视野再放广一点:在工作中,我