【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题

文章目录

  • Android 设备的CPU类型(通常称为"ABIs")
  • 一、 问题描述
  • 二、解决方法
    • 2.1、解决之前的截图:
    • 2.2、解决后的截图:
    • 2.3、解决方法:
    • 2.4、建议
  • 三、扩展知识
    • 3.1App中可能出错的地方
    • 3.2 其他地方也可能出错
      • 3.2.1 混合使用不同C++运行时编译的.so文件
      • 3.2.2 没有为每个支持的CPU架构提供对应的.so文件
      • 3.2.3 只提供armeabi架构的.so文件而忽略其他ABIs的
  • 四、关于google强制64位架构的规定
    • 4.1 是否包含 64 位?
    • 4.2 判断是否支持 64 位架构
      • 4.2.1. AS 的 APK 分析器
      • 4.2.2 使用 zipinfo 命令进行分析
    • 4.3 在 64 位设备上测试应用
  • 五、更多参考

Android 设备的CPU类型(通常称为"ABIs")

  1. armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年12月以后的生产的大部分Android设备都使用它.
  2. arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
  3. armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
  4. x86: 平板、模拟器用得比较多。
  5. x86_64: 64位的平板。

更多可以查看链接 https://developer.android.google.cn/ndk/guides/abis

一、 问题描述

今天测试人员测试集成版本时除了一个bug:关于华为 Mate 8手机Android 6.0系统运行刚刚提测的版本时,出现闪退的bug,而小米 4 手机Android 6.0系统却没有出现任何bug,运行良好。后来查看本人相关模块的代码,发现本人集成版本相关模块的代码和分支版本相关模块的代码是一模一样的,那就是说本人把分支代码合并到主干代码是没有问题的,所以去查看主干代码的问题。

经过一番查看提交日志,发现有位同事再我合并代码之前,提交了一个关于友盟推送的so文件的记录,原来他加入了一个arm64-v8a文件夹,里面有友盟推送的arm64-v8a的so库文件。而其他的so库文本却没有arm64-v8a对应的版本。

通过百度查到知乎有一段关于arm64-v8a的解释:

arm64-v8a是可以向下兼容的,但前提是你的项目里面没有arm64-v8a的文件夹,如果你有两个文件夹armeabi和arm64-v8a,两个文件夹,armeabi里面有a.so 和 b.so,arm64-v8a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有arm64-v8a的文件夹,发现里面没有b.so,就报错了,所以这个时候删掉arm64-v8a文件夹,这个时候手机发现没有适配arm64-v8a,就会直接去找armeabi的so库,所以要么你别加arm64-v8a,要么armeabi里面有的so库,arm64-v8a里面也必须有

作者:green jim
链接:http://www.zhihu.com/question/36893314/answer/78467097
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发现原来华为 Mate 8手机是64位的操作系统,而小米 4 手机是32位的操作系统,所以小米 4 手机手机运行APP没bug,而华为 Mate 8手机运行APP出现闪退bug。

二、解决方法

2.1、解决之前的截图:

【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第1张图片
从截图可以看出来,第一个项目中有 arm64-v8a,而没有x86目录,第二个项目中没有 arm64-v8a,而有x86目录。第一个项目是作为项目引用导入到第二个项目中的。

2.2、解决后的截图:

【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第2张图片
从截图可以看出来,第一个项目中和第二个项目中没有的libs目录下,都是armeabi-v7a、armeabi、x86三个目录,保持一致。第一个项目是作为项目引用导入到第二个项目中的。

2.3、解决方法:

解决方法是:从友盟官方中去下载x86的相关so文件,放在x86目录下,把arm64-v8a目录删除。将所有关于so文件的都要保持一致,即:如果你要添加一个armeabi-v8a目录,下面放第三方的armeabi-v8a相关的so文件,那么你其他的so文件都要有相应想armeabi-v8a版本,不然就会报错。

64 位的 arm64-v8a 是可以向下兼容的,但是这有个前提,那就是如果你的项目中,有 armeabi-v7a 和 arm64-v8a 两个目录,就需要保证这两个目录下支持的 so 库文件保持一致。

【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第3张图片

在左边的情况下,如果 arm64-v8a 的手机用到 b.so 时,就会去 arm64-v8a 目录下找,当然是找不到 b.so 文件的,就会直接抛异常,而不会再去 armeabi-v7a 目录下继续寻找。

如果需要提供多套 ABIs 的支持,就需要保证所有 ABI 目录下,对应的 so 文件保持一致。

2.4、建议

来自于博客:《与 .so 有关的一个长年大坑 》给的建议是:

  • 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 .so 数量一致
  • 对只提供 armeabi 版本的第三方 .so,原样复制一份到 armeabi-v7a 文件夹


三、扩展知识

下面文章转载于asce1885(简书作者):关于Android的.so文件你所需要知道的
(原文链接:http://www.jianshu.com/p/cb05698a1968)
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

早期的Android系统几乎只支持ARMv5的CPU架构,你知道现在它支持多少种吗?7种!

Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。

应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

##为什么你需要重点关注.so文件
如果项目中使用到了NDK,它将会生成.so文件,因此显然你已经在关注它了。如果只是使用Java语言进行编码,你可能在想不需要关注.so文件了吧,因为Java是跨平台的。但事实上,即使你在项目中只是使用Java语言,很多情况下,你可能并没有意识到项目中依赖的函数库或者引擎库里面已经嵌入了.so文件,并依赖于不同的ABI。

例如,项目中使用RenderScript支持库,OpenCV,Unity,android-gif-drawable,SQLCipher等,你都已经在生成的APK文件中包含.so文件了,而你需要关注.so文件。

Android应用支持的ABI取决于APK中位于lib/ABI目录中的.so文件,其中ABI可能是上面说过的七种ABI中的一种。
【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第4张图片

Native Libs Monitor这个应用可以帮助我们理解手机上安装的APK用到了哪些.so文件,以及.so文件来源于哪些函数库或者框架。

当然,我们也可以自己对app反编译来获取这些信息,不过相对麻烦一些。

很多设备都支持多于一种的ABI。例如ARM64和x86设备也可以同时运行armeabi-v7a和armeabi的二进制包。但最好是针对特定平台提供相应平台的二进制包,这种情况下运行时就少了一个模拟层(例如x86设备上模拟arm的虚拟层),从而得到更好的性能(归功于最近的架构更新,例如硬件fpu,更多的寄存器,更好的向量化等)。

我们可以通过Build.SUPPORTED_ABIS得到根据偏好排序的设备支持的ABI列表。但你不应该从你的应用程序中读取它,因为Android包管理器安装APK时,会自动选择APK包中为对应系统ABI预编译好的.so文件,如果在对应的lib/ABI目录中存在.so文件的话。

3.1App中可能出错的地方

处理.so文件时有一条简单却并不知名的重要法则。

你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。

当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装。在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。

3.2 其他地方也可能出错

当你引入一个.so文件时,不止影响到CPU架构。我从其他开发者那里可以看到一系列常见的错误,其中最多的是"UnsatisfiedLinkError","dlopen: failed"以及其他类型的crash或者低下的性能:

###使用android-21平台版本编译的.so文件运行在android-15的设备上
使用NDK时,你可能会倾向于使用最新的编译平台,但事实上这是错误的,因为NDK平台不是后向兼容的,而是前向兼容的。推荐使用app的minSdkVersion对应的编译平台。

这也意味着当你引入一个预编译好的.so文件时,你需要检查它被编译所用的平台版本。

3.2.1 混合使用不同C++运行时编译的.so文件

.so文件可以依赖于不同的C++运行时,静态编译或者动态加载。混合使用不同版本的C++运行时可能导致很多奇怪的crash,是应该避免的。作为一个经验法则,当只有一个.so文件时,静态编译C++运行时是没问题的,否则当存在多个.so文件时,应该让所有的.so文件都动态链接相同的C++运行时。

这意味着当引入一个新的预编译.so文件,而且项目中还存在其他的.so文件时,我们需要首先确认新引入的.so文件使用的C++运行时是否和已经存在的.so文件一致。

3.2.2 没有为每个支持的CPU架构提供对应的.so文件

这一点在前文已经说到了,但你应该真的特别注意它,因为它可能发生在根本没有意识到的情况下。

例如:你的app支持armeabi-v7a和x86架构,然后使用Android Studio新增了一个函数库依赖,这个函数库包含.so文件并支持更多的CPU架构,例如新增android-gif-drawable函数库:

compile ‘pl.droidsonroids.gif:android-gif-drawable:1.1.+’

发布我们的app后,会发现它在某些设备上会发生Crash,例如Galaxy S6,最终可以发现只有64位目录下的.so文件被安装进手机。

解决方案:重新编译我们的.so文件使其支持缺失的ABIs,或者设置

ndk.abiFilters

显示指定支持的ABIs。

最后一点:如果你是一个SDK提供者,但提供的函数库不支持所有的ABIs,那你将会搞砸你的用户,因为他们能支持的ABIs必将只能少于你提供的。

###将.so文件放在错误的地方
我们往往很容易对.so文件应该放在或者生成到哪里感到困惑,下面是一个总结:

  • Android Studio工程放在jniLibs/ABI目录中(当然也可以通过在build.gradle文件中的设置jniLibs.srcDir属性自己指定)
  • Eclipse工程放在libs/ABI目录中(这也是ndk-build命令默认生成.so文件的目录)
  • AAR压缩包中位于jni/ABI目录中(.so文件会自动包含到引用AAR压缩包的APK中)
  • 最终APK文件中的lib/ABI目录中
  • 通过PackageManager安装后,在小于Android 5.0的系统中,.so文件位于app的nativeLibraryPath目录中;在大于等于Android 5.0的系统中,.so文件位于app的nativeLibraryRootDir/CPU_ARCH目录中。

3.2.3 只提供armeabi架构的.so文件而忽略其他ABIs的

所有的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件,因此似乎移除其他ABIs的.so文件是一个减少APK大小的好技巧。但事实上并不是:这不只影响到函数库的性能和兼容性。

x86设备能够很好的运行ARM类型函数库,但并不保证100%不发生crash,特别是对旧设备。64位设备(arm64-v8a, x86_64, mips64)能够运行32位的函数库,但是以32位模式运行,在64位平台上运行32位版本的ART和Android组件,将丢失专为64位优化过的性能(ART,webview,media等等)。

以减少APK包大小为由是一个错误的借口,因为你也可以选择在应用市场上传指定ABI版本的APK,生成不同ABI版本的APK可以在build.gradle中如下配置:

android {
    ... 
    splits {
        abi {
            enable true
            reset()
            include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
            universalApk true //generate an additional APK that contains all the ABIs
        }
    }

    // map for the version code
    project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]

    android.applicationVariants.all { variant ->
        // assign different version code for each output
        variant.outputs.each { output ->
            output.versionCodeOverride =
                    project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
        }
    }
 }

四、关于google强制64位架构的规定

ps: 下面的内容,部分摘自: https://mp.weixin.qq.com/s/pFuaABbQDwnPqf3BIcWTmg
更新自2019年6月

【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第5张图片

早在今年(2019)一月份,Google 就发布通知,在今年 8 月 1 日开始,上架的 App,除了提供 32 位的版本之外,还需要提供 64 位的版本。

因此,项目之前强制只使用armeabi一种架构的方式已经不行了。

  • 那这里说的 64 位版本支持,到底是什么?

如果你的应用,完全是使用 Java 或者 Kotlin 编写代码,不包含任何原生(Native)的支持,那么就表示这个应用已经支持 64 位。

但是应用内使用了任何原生(Native)的支持(so 库),就需要针对这些 so 文件,针对不同的 CPU 架构提供不同的版本的 so 支持。

需要注意的是,有些时候,在我们自身的代码中,确实没有用到原生的支持,但是在 App 中使用的一些第三方库中却包含了。

此时最稳妥的方式,就是针对最终打包生成的 APK 文件进行分析,来判断是否需要提供 64 位架构的支持。

4.1 是否包含 64 位?

从前文中应该了解到,支持对应的 ABIs,反映在项目中,就是存在对应 ABIs 架构的目录,并且目录中有完备的 so 库支持。

Google 并不要求我们支持所有的 64 位架构,但是对于已经支持的每种原生 32 位架构,就必须包含对应的 64 位架构。

例如:

  • 对于 ARM 架构,有 armeabi-v7a(32位) 就必须 arm64-v8a(64位)。
  • 对于 x86 架构,有 x86(32位) 就必须有 x86_64(64位)

这就要求我们有对应的目录,并且目录中包含对应的 so 文件。APK 中提供了完备的 ABIs 支持,运行的之后,会选取对应的最优支持进行加载和使用。

需要注意的是,有时候我们将 32 位的 so 复制到 64 位中,运行不会出现异常,但是这依然存在隐患。最好的办法是根据不同的架构,编译对应的 so 文件,原则上,我们的目标是确保应用可以在仅支持 64 位架构的环境中正常运行。

4.2 判断是否支持 64 位架构

前面也提到,我们的项目中,可能会引入一些第三方库,导致在不明确的情况下,引入了一些预期之外的 ABIs 库。

通常我们的做法是在 Gradle 中增加 abiFilters 过滤,来确保不会在打包输出的 APK 中存在预期之外的 ABIs 目录和 so 库。

    ndk {
        //设置支持的SO库架构
        abiFilters 'armeabi-v7a' 
    }

那么我们拿最终打包输出的 APK 文件去分析,是最稳妥的办法。

分析的方法有两种:

4.2.1. AS 的 APK 分析器

在 Android Studio 中,从菜单依次选择 Build → Analyze APK…

【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第6张图片
选择需要分析的 APK 文件,查看其 lib 目录,是否存在预期的 ABIs 目录以及完备的 so 文件。
【我的Android进阶之旅】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题_第7张图片

4.2.2 使用 zipinfo 命令进行分析

得到待分析的 APK 文件,就可以通过 zipinfo + grep 命令,输出其内包含的 so 文件。

> zipinfo -1 YOUR_APK_FILE.apk | grep \.so$
    lib/armeabi-v7a/libmain.so
    lib/armeabi-v7a/libmono.so
    lib/armeabi-v7a/libunity.so
    lib/arm64-v8a/libmain.so
    lib/arm64-v8a/libmono.so
    lib/arm64-v8a/libunity.so

依然是去看对应目录和 so 文件是否完备。

4.3 在 64 位设备上测试应用

支持 64 位架构是为了让我们利用 CPU 的特性,以提升性能,但是稳定依然是我们首先要保证的,所以在升级之后,就需要进行测试。

要测试 App,最简单的方式是使用 adb 命令安装该应用,可以配合 --abi 参数,用以指示要将那些 so 库,安装到设备上,这样我们在这个设备上安装的 App,就会仅包含我们制定的库。

# 成功安装 APK :
> adb install --abi armeabi-v7a YOUR_APK_FILE.apk
Success

# 如果 APK 中不包含 64 位 so 文件:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
adb: failed to install YOUR_APK_FILE.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]

# 如果你的设备(手机)不支持 64 位架构
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
ABI arm64-v8a not supported on this device

去年上市的手机,大部分都是 64 位架构的,找一款来测试即可。

五、更多参考

  • 安卓so库你应该注意的事
    (http://www.voidcn.com/blog/u013278099/article/p-4944290.html)

  • Android开发,不可不知的so文件知识大全
    http://alphayang.community/2015/11/27/so-files-guide/

  • 与 .so 有关的一个长年大坑
    https://zhuanlan.zhihu.com/p/21359984

  • Android jniLibs下目录详解(.so文件)
    http://www.jianshu.com/p/b758e36ae9b5

  • ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57
    http://www.myir-tech.com/resource/448.asp

  • ARM architecture
    https://en.wikipedia.org/wiki/ARM_architecture

  • android的armeabi和armeabi-v7a
    http://gybin.iteye.com/blog/2031565

  • 这一次谷歌很强硬,App 再不支持,两个月后将拒绝上架!
    https://mp.weixin.qq.com/s/pFuaABbQDwnPqf3BIcWTmg

  • Android使用 SO 库时要注意的一些问题
    http://www.apkbus.com/home.php?mod=space&uid=705730&do=blog&id=61719

版权声明:本文为【欧阳鹏】原创文章,欢迎转载,转载请注明出处! 【http://blog.csdn.net/ouyang_peng/article/details/51168072】

这里写图片描述

你可能感兴趣的:(Android应用开发,我的Android进阶之旅)