专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
开发者全社区  ·  GPT-5将免费向用户开放,DeepSeek逼的 ·  6 小时前  
开发者全社区  ·  公务员性价比正在消失 ·  昨天  
开发者全社区  ·  清华美女状元嫁初中黄毛 ·  2 天前  
开发者全社区  ·  小作文频出!某基金经理晕厥 ·  3 天前  
51好读  ›  专栏  ›  开发者全社区

Android使用 SO 库时要注意的一些问题

开发者全社区  · 公众号  · android  · 2016-12-30 09:58

正文

相关阅读:

吊炸天!74款APP完整源码!

有了这些免费无限次的API 接口,再也不愁没有服务器开发不了APP了,也可以自己开发小程序了

github上万颗star的21个JAVA(19个Android相关)开发框架,知道多少,用过多少(截止2016年12月17日)


来自:http://www.apkbus.com/home.php?mod=space&uid=705730&do=blog&id=61719


常和 SO 库开发打交道的同学来说已经是老生长谈,但是既然要讨论一整个动态加载系列,我想还是有必要说说使用 SO 库时的一些问题。

在项目里使用 SO 库非常简单,在 加载 SD 卡中的 SO 库 中也有谈到,只需要把需要用到的 SO 库拷贝进 jniLibs(或者 Eclipse 项目里面的 libs) 中,然后在 JAVA 代码中调用 System.loadLibrary(“xxx”) 加载对应的 SO 库,就可以使用 JNI 语句调用 SO 库里面的 Native 方法了。

但是有同学注意到了,SO 库文件可以随便改文件名,却不能任意修改文件夹路径,而是 “armeabi”、“armeabi-v7a”、“x86” 等文件夹名有着严格的要求,这些文件夹名有什么意义么?

SO 库类型和 CPU 架构类型

原因很简单,不同 CPU 架构的设备需要用不同类型 SO 库(从文件名也可以猜出来个大概嘛 ╮( ̄▽ ̄”)╭)。

记得还在学校的时候,提及 ARM 处理器时,老师说以后移动设备的 CPU 基本就是 ARM 类型的了。老师不曾欺我,早期的 Android 系统几乎只支持 ARM 的 CPU 架构,不过现在至少支持以下七种不同的 CPU 架构:ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64 和 x86_64。每一种 CPU 类型都对应一种 ABI(Application Binary Interface),“armeabi-v7a”文件夹前面的 “armeabi” 指的就是 ARM 这种类型的 ABI,后面的 “v7a” 指的是 ARMv7。这 7 种 CPU 类型对应的 SO 库的文件夹名是:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

不同类型的移动设备在运行 APP 时,需要加载自己支持的类型的 SO 库,不然就 GG 了。通过 Build.SUPPORTED_ABIS 我们可以判断当前设备支持的 ABI,不过一般情况下,不需要开发者自己去判断 ABI,Android 系统在安装 APK 的时候,不会安装 APK 里面全部的 SO 库文件,而是会根据当前 CPU 类型支持的 ABI,从 APK 里面拷贝最合适的 SO 库,并保存在 APP 的内部存储路径的 libs 下面。(这里说一般情况,是因为有例外的情况存在,比如我们动态加载外部的 SO 库的时候,就需要自己判断 ABI 类型了。)

一种 CPU 架构 = 一种对应的 ABI 参数 =  一种对应类型的 SO 库

到这里,我们发现使用 SO 库的逻辑还是比较简单的,但是 Android 系统加载 SO 库的逻辑还是给我们留下了一些坑。

使用 SO 库时要注意的一些问题

1. 别把 SO 库放错地方

SO 库其实都是 APP 运行时加载的,也就是说 APP 只有在运行的时候才知道 SO 库文件的存在,这就无法通过静态代码检查或者在编译 APP 时检查 SO 库文件是否正常。所以,Android 开发对 SO 库的存放路径有严格的要求。

使用 SO 库的时候,除了 “armeabi-v7a” 等文件夹名需要严格按照规定的来自外,SO 库要放在项目的哪个文件夹下也要按照套路来,以下是一些总结:

使用 SO 库的时候,除了 “armeabi-v7a” 等文件夹名需要严格按照规定的来自外,SO 库要放在项目的哪个文件夹下也要按照套路来,以下是一些总结:

  • Android Studio 工程放在 jniLibs/xxxabi 目录中(当然也可以通过在 build.gradle 文件中的设置 jniLibs.srcDir 属性自己指定);

  • Eclipse 工程放在 libs/xxxabi 目录中(这也是使用 ndk-build 命令生成 SO 库的默认目录);

  • aar 依赖包中位于 jni/ABI 目录中(SO 库会自动包含到引用 AAR 压缩包到 APK 中);

  • 最终构建出来的 APK 文件中,SO 库存在 lib/xxxabi 目录中(也就是说无论你用什么方式构建,只要保证 APK 包里 SO 库的这个路径没错就没问题);

  • 通过 PackageManager 安装后,在小于 Android 5.0 的系统中,SO 库位于 APP 的 nativeLibraryPath 目录中;在大于等于 Android 5.0 的系统中,SO 库位于 APP 的 nativeLibraryRootDir/CPU_ARCH 目录中;

既然扯到了这里,顺便说一下,我在使用 Android Studio 1.5 构建 APK 的时候,发现 Gradle 插件只会默认打包 application 类型的 module 的 jniLibs 下面的 SO 库文件,而不会打包 aar 依赖包的 SO 库,所以会导致最终构建出来的 APK 里的 SO 库文件缺失。暂时的解决方案是把所有的 SO 库都放在 application 模块中(这显然不是很好的解决方案),不知道这是不是 Studio 的 BUG,同事的解决方案是通过修改 Gradle 插件来增加对 aar 依赖包的 SO 库的打包支持(GitHub 有开源的第三方 Gradle 插件项目,使用 Java 和 Groovy 语言开发)。

2. 尽可能提供 CPU 支持的最优 SO 库

当一个应用安装在设备上,只有该设备支持的 CPU 架构对应的 SO 库会被安装。但是,有时候,设备支持的 SO 库类型不止一种,比如大多的 X86 设备除了支持 X86 类型的 SO 库,还兼容 ARM 类型的 SO 库(目前应用市场上大部分的 APP 只适配了 ARM 类型的 SO 库,X86 类型的设备如果不能兼容 ARM 类型的 SO 库的话,大概要嗝屁了吧)。

所以如果你的 APK 只适配了 ARM 类型的 SO 库的话,还是能以兼容的模式在 X86 类型的设备上运行(比如华硕的平板),但是这不意味着你就不用适配 X86 类型的 SO 库了,因为 X86 的 CPU 使用兼容模式运行 ARM 类型的 SO 库会异常卡顿(试着回想几年前你开始学习 Android 开发的时候,在 PC 上使用 AVD 模拟器的那种感觉)。

3. 注意 SO 库的编译版本

除了要注意使用了正确 CPU 类型的 SO 库,也要注意 SO 库的编译版本的问题。虽然现在的 Android Studio 支持在项目中直接编译 SO 库,但是更多的时候我们还是选择使用事先编译好的 SO 库,这时就要注意了,编译 APK 的时候,我们总是希望使用最新版本的 build-tools 来编译,因为 Android SDK 最新版本会帮我们做出最优的向下兼容工作。

但是这对于编译 SO 库来说就不一样了,因为 NDK 平台不是向下兼容的,而是向上兼容的。应该使用 app 的 minSdkVersion 对应的版本的 NDK 标本来编译 SO 库文件,如果使用了太高版本的 NDK,可能会导致 APP 性能低下,或者引发一些 SO 库相关的运行时异常,比如 “UnsatisfiedLinkError”,“dlopen: failed” 以及其他类型的Crash。

一般情况下,我们都是使用编译好的 SO 库文件,所以当你引入一个预编译好的 SO 库时,你需要检查它被编译所用的平台版本。

4. 尽可能为每种 CPU 类型都提供对应的 SO 库

比如有时候,因为业务的需求,我们的 APP 不需要支持 AMR64 的设备,但这不意味着我们就不用编译 ARM64 对应的 SO 库。举个例子,我们的 APP 只支持 armeabi-v7a 和 x86 架构,然后我们的 APP 使用了一个第三方的 Library,而这个 Library 提供了 AMR64 等更多类型 CPU 架构的支持,构建 APK 的时候,这些 ARM64 的 SO 库依然会被打包进 APK 里面,也就是说我们自己的 SO 库没有对应的 ARM64 的 SO 库,而第三方的 Library 却有。这时候,某些 ARM64 的设备安装该 APK 的时候,发现我们的 APK 里带有 ARM64 的 SO 库,会误以为我们的 APP 已经做好了 AMR64 的适配工作,所以只会选择安装 APK 里面 ARM64 类型的 SO 库,这样会导致我们自己项目的 SO 库没有被正确安装(虽然 armeabi-v7a 和 x86 类型的 SO 库确实存在 APK 包里面)。

这时正确的做法是,给我们自己的 SO 库也提供 AMR64 支持,或者不打包第三方 Library 项目的 ARM64 的 SO 库。使用第二种方案时,可以把 APK 里面不需要支持的 ABI 文件夹给删除,然后重新打包,而在 Android Studio 下,则可以通过以下的构建方式指定需要类型的 SO 库。

productFlavors {
    flavor1 {
        ndk {
            abiFilters "armeabi-v7a"
            abiFilters "x86"
            abiFilters "armeabi"
        }
    }
    flavor2 {
        ndk {
            abiFilters "armeabi-v7a"
            abiFilters "x86"
            abiFilters "armeabi"
            abiFilters "arm64-v8a"
            abiFilters "x86_64"
        }
    }
}

需要说明的是,如果我们的项目是 SDK 项目,我们最好提供全平台类型的 SO 库支持,因为 APP 能支持的设备 CPU 类型的数量,就是项目中所有 SO 库支持的最少 CPU 类型的数量(使用我们 SDK 的 APP 能支持的 CPU 类型只能少于等于我们 SDK 支持的类型)。







请到「今天看啥」查看全文