关于android:为何大厂APP如微信支付宝淘宝手Q等只适配了armeabiv7aarmeabi

47次阅读

共计 5986 个字符,预计需要花费 15 分钟才能阅读完成。

文章首发于公众号 「技术最 TOP」,每天都有干货文章继续更新,能够微信搜寻「技术最 TOP」 第一工夫浏览!

0. 前言

前几天啊,在公众号发了一篇文章《优化 ApK 大小之 ABI Filters 和 APK split》,评论区收到了一些留言说,文章讲得不够深刻,对于零碎是如何抉择不同 abi 下的 so 库的?以后 APP 该如何适配?该去掉哪些该保留哪些?都存在一些疑难。

因而,决定亲自更文一篇,系统地讲一下对于 Android CPU 架构方面的一些货色,以及联合大厂 APP 如微信、支付宝、淘宝等 APP 的适配状况,剖析咱们开发 APP 中该如何适配。本文波及以下几个问题:

  • 什么是 ABI?
  • ABI 有何作用?
  • 目前大厂 APP 是如何适配不同的 CPU 架构的?
  • ABI 是如何工作的?
  • 咱们本人的 APP 中该如何适配?
  • ABI split- 性能 + 兼容全都要

本篇文章中,就一一为你解答这些疑难。

1. 什么是 ABI?

ABI 是英文 Application Binary Interface 的缩写,及利用二进制接口。

不同 Android 设施,应用的 CPU 架构可能不同,因而反对不同的指令集。CPU 与指令集的每种组合都有其本人的利用二进制界面(或 ABI),ABI 十分准确地定义了应用程序的机器代码应如何在运行时与零碎交互。您必须为要与您的应用程序一起应用的每种 CPU 架构指定一个 ABI(Application Binary Interface)。

ABI 蕴含以下信息:

  • 可应用的 CPU 指令集(和扩大指令集)。
  • 运行时内存存储和加载的字节程序。Android 始终是 little-endian。
  • 在利用和零碎之间传递数据的标准(包含对齐限度),以及零碎调用函数时如何应用堆栈和寄存器。
  • 可执行二进制文件(例如程序和共享库)的格局,以及它们反对的内容类型。Android 始终应用 ELF。
  • 如何重整 C++ 名称。

    Android 目前反对以下 7 种 ABIs:

    mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a

    2. ABI 有何作用?

    当咱们想要在我的项目中应用 native(C/C++)类库, 咱们必须对要反对的处理器架构提供对应编译包。每个处理器架构须要咱们提供一个或多个蕴含 native 代码的.so 文件。

默认状况下,为了使 APP 有更好的兼容性,咱们应用 Android Studio 或者命令打包时,会默认反对所有的架构,但相应的 APK size 会疯狂的增大。对于用户来说,指标设施只须要其中一个版本,但当用户下载 APK 时,会全副下载(对用户来说相当的不敌对)。

怎么办呢?abifilters 为咱们提供了解决方案,abifilters为咱们提供了抉择适配指定 CPU 架构的能力,只须要在 app 下的 build.gradle 增加如下配置:

android {
        defaultConfig {
            ndk {abiFilters 'arm64-v8a', 'x86_64'}
        }
    
}

你可能看了下面的这些文字,还不能了解 abi 的作用,那么咱们就用一个简略的例子来阐明一下。

2.1 举例说明 ABI 的作用

首先,咱们创立一个最简略的 Hello world 利用,只有一个 Activity 和一个启动图标。咱们看以下打进去的 apk:

没有任何的原生库应用,大小为2.1MB,当初咱们为它增加多 ABI 原生库反对,咱们在我的项目中集成Realm,而后打包。

看到没,apk 大小从 2.1MB 猛减少到11.2MB, 多了一个原生 so 库的文件夹,大小为8.8MB,咱们来看一下它的详细信息:

如上图所示,Realm为 5 种 CPU 架构生成了 .so 库,别离是 mipsx86x86_64arm64-v8aarmeabi-v7a。减少了8.8MB 包的大小。然而这不是咱们想要的,咱们只想要适配咱们指定的的 CPU 架构,因而,咱们须要在 gralde.build 中增加 abifilters 配置来实现咱们想要的成果:

android {
    compileSdkVersion 28 // 编译 sdk 版本
    defaultConfig {
        applicationId "com.example.zhouwei.helloworld"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        // 适配指定 CPU 架构
        ndk {abiFilters 'arm64-v8a', 'x86_64'}
    }
}

成果如下:

能够看到,只生成了咱们指定 CPU 架构的 so 文件,包的大小也缩小了5.3MB

这时候,你可能会有一个疑难,Android 共反对 7 种 CPU 架构,那么,咱们在理论我的项目中该适配哪些 CPU 架构能保障最好的兼容,同时又最大限度的缩小 APK 的大小?

在答复这个问题之前,咱们无妨看一下这些顶级巨头公司,他们是是如何适配的。

3. 目前大厂 APP 是如何适配不同的 CPU 架构的?

首先,咱们下载一些大厂的 APK,看一下他们的适配状况,这里我剖析了微信、手机 QQ、支付宝和淘宝这 4 个 APP 的适配状况:

能够看到,微信适配的是arm64-v8a(微信应该是最近才适配到arm64-v8a, 以前是armeabi), 支付宝和手 Q 适配的是armwabi,淘宝适配的是armwabi-v7a。各个 APP 适配的平台不太一样,然而他们有一个共同点,那就是它们只指定了一个平台。

等等,下面这些 APP 只适配了一中 CPU 架构,比方只适配了 armwabi-v7a, 那如果 APP 装在其余架构的手机上,如arm64-v8a 上,会蹦吗?

要弄清楚这个问题,咱们得先搞清楚,ABI 是如何工作的。

ABI 是如何工作的呢?

官网文档解释如下:

Android 零碎在运行时晓得它反对哪些 ABI,因为版本特定的零碎属性会批示:

  • 设施的次要 ABI,与零碎映像自身应用的机器代码对应。
  • (可选)与零碎映像也反对的其余 ABI 对应的辅助 ABI。

此机制确保零碎在装置时从软件包提取最佳机器代码。

为实现最佳性能,应间接针对次要 ABI 进行编译。例如,基于 ARMv5TE 的典型设施只会定义主 ABI:armeabi。相同,基于 ARMv7 的典型设施将主 ABI 定义为 armeabi-v7a,并将辅助 ABI 定义为 armeabi,因为它能够运行为每个 ABI 生成的利用原生二进制文件。

64 位设施也反对其 32 位变体。以 arm64-v8a 设施为例,该设施也能够运行 armeabi 和 armeabi-v7a 代码。但请留神,如果利用以 arm64-v8a 为指标,而非依赖于运行 armeabi-v7a 版利用的设施,则利用在 64 位设施上的性能要好得多。

许多基于 x86 的设施也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设施,主 ABI 将是 x86,辅助 ABI 是 armeabi-v7a。

下面这一段是不是有点看蒙了,这里我来简略解释以下。总的来说,就是一个 Android 设施能够反对多种 ABI, 设施主 ABI 和辅助 ABI, 以 arm64-v8a 为主 ABI 的设施,辅助 ABI 为 armeabi-v7aarmeabi,以 armeabi-v7a 为主 ABI 的设施,辅助 ABI 为armeabi

另外,x86 架构的手机都会蕴含由 Intel 提供的称为 Houdini 的指令集动静转码工具,实现对 arm .so 的兼容,也就是说有适配 armeabi 平台的 APP 是能够跑在 x86 手机上的。

3.1 主辅助 ABI 具体适配流程

后面说了 ABI 的工作原理,一个 Android 设施反对主辅 ABI, 那么他们具体是如何工作的呢?咱们以 arm64-v8a 架构的手机为例:

对于一个 cpu 是 arm64-v8a 架构的手机,它运行 app 时,进入 jnilibs 去读取库文件时,先看有没有 arm64-v8a 文件夹,如果没有该文件夹,去找 armeabi-v7a 文件夹,如果没有,再去找 armeabi 文件夹,如果连这个文件夹也没有,就抛出异样;

如果有 arm64-v8a 文件夹,那么就去找特定名称的.so 文件,留神:如果没有找到想要的 .so 文件,不会再往下(armeabi-v7a 文件夹)找了,而是间接抛出异样。

Exception:Java.lang.UnsatisfiedLinkError: dlopen failed: library“/***.so”not found 

特地须要留神的状况是在命中了文件夹,而未命中 so 文件这种状况:

  • 比方命中了 arm64-v8a 文件夹,没有找到须要的 so 文件,就不会再往下(armeabi-v7a 文件夹)找了,而是间接抛出异样。
  • 如果你的我的项目用到了第三方依赖,如果只保留一个 ABI 的时候,倡议在 Build 中退出 ndk.abiFilters
  • 例如:第三方 aar 文件,如果这个 sdk 对 abi 的反对比拟全,可能会蕴含 armeabi、armeabi-v7a、x86、arm64-v8a、x86_64 五种 abi,而你利用的其它 so 只反对 armeabi、armeabi-v7a、x86 三种,间接援用 sdk 的 aar,会主动编译出反对 5 种 abi 的包。然而利用的其它 so 短少对其它两种 abi 的反对,那么如果利用运行于 arm64-v8a、x86_64 为首选 abi 的设施上时,就会 ==crash== 了哦。

因而,咱们须要在咱们的 app 中配置 abiFilter 配置,来防止一些未知的谬误。

defaultConfig {  
    ndk {abiFilters "armeabi"// 指定 ndk 须要兼容的 ABI(这样其余依赖包里 x86,armeabi,arm-v8 之类的 so 会被过滤掉) 
    }  
}
3.2 Android 7 种 CPU 架构在以后市场的占有率
  • arm64-v8a: 目前支流版本
  • armeabi-v7a: 一些老旧的手机
  • x86 / x86_64: x86 架构的手机都会蕴含由 Intel 提供的称为 Houdini 的指令集动静转码工具,实现对 arm .so 的兼容,再思考 x86 1% 以下的市场占有率,x86 相干的两个 .so 也是能够疏忽的
  • armeabi/mips / mips64: NDK 以前反对 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已不再反对,极少用于手机能够忽。

目前手机市场上,x86 / x86_64/armeabi/mips / mips6 的架构,根本能够不不思考了,它们的占有量应很少很少了,arm64-v8a作为最新一代架构,应该是目前的支流 armeabi-v7a 只存在少部分老旧手机。

我试着在 Google 上查找,具体的市场占有数据,但没找到,然而从国民级利用微信只适配 arm64-v8a 就能够看出,arm64-v8a是目前的支流,并且还有一点,Google Play 从 2019 年 8 月开始,就强制 APP 适配arm64-v8a,以缓缓淘汰 32 位的armeabi-v7a

4. 咱们我的项目中该如何适配呢?

这里就能够答复后面的两个问题了。

Q1:只适配了 armwabi-v7a, 那如果 APP 装在其余架构的手机上,如arm64-v8a 上,会蹦吗?

A: 不会,然而反过来会。

因为 armwabi-v7aarm64-v8a会向下兼容:

  • 只适配 armeabi 的 APP 能够跑在 armeabi,x86,x86_64,armwabi-v7a,arm64-v8
  • 只适配 armwabi-v7a 能够运行在 armwabi-v7aarm64-v8a
  • 只适配 arm64-v8a 能够运行在arm64-v8a

那咱们该如何适配呢?给出如下几个计划:

计划一:只适配armeabi

  • 长处:基本上适配了全副 CPU 架构(除了淘汰的 mips 和 mips_64)
  • 毛病:性能低,相当于在绝大多数手机上都是须要辅助 ABI 或动静转码来兼容

计划二:只适配 armwabi-v7a

同理计划一,只是又筛掉了一部分老旧设施, 在性能和兼容二者中比拟均衡

计划三: 只适配 arm64-v8

  • 长处: 性能最佳
  • 毛病: 只能运行在 arm64-v8 上,要放弃局部老旧设施用户

这三种计划都是能够的,当初的大厂 APP 适配中,这三种都有,大部分是前 2 种计划。具体选哪一种就看本人的考量了,以性能换兼容就arm64-v8, 以兼容换性能armeabi, 二者略微均衡一点的就armwabi-v7a

目前来说,大多数的大厂 APP 用的都是 armeabiarmwabi-v7a,只有像微信这种牛逼的 APP, 为了谋求性能和用户体验,放弃了少部分设施,这也说得通吧,毕竟微信也不在乎苍蝇那点肉。

5. 番外篇 - 性能 + 兼容是否兼得?

其实到上一大节,本文就该完结了,但总感觉长处意犹未尽,除了适配所有全副 CPU 架构外,就特么不能性能和兼容同时兼得吗?其实 Google 早有思考。也是能够实现的那就是 abi split,分包,实现也很简略,在 gradle 中增加如下配置:

 android {
      ...
      splits {

        // Configures multiple APKs based on ABI.
        abi {

          // Enables building multiple APKs per ABI.
          enable true

          // By default all ABIs are included, so use reset() and include to specify that we only
          // want APKs for x86 and x86_64.

          // Resets the list of ABIs that Gradle should create APKs for to none.
          reset()

          // Specifies a list of ABIs that Gradle should create APKs for.
          include "x86", "x86_64", "arm64-v8a", "armeabi", "armeabi-v7a"

          // Specifies that we do not want to also generate a universal APK that includes all ABIs.
          universalApk false
        }
      }
    }

而后,就能为每个 CPU 架构独自打一个 APK,该 apk 中就只蕴含一个平台,如下:

这样,又能保障性能,又能不额定减少 APK 的大小,同时又又很完满的兼容,因为能够为所有架构都独自打一个包,一举多得。

同时,Google Play 反对上传多个 APK:

这样,就能依据不同的 CPU 架构,下载不同的包啦!

然而,然而,然而,很遗憾,国内的利用商店目前还不反对!

参考文章

  • https://www.diycode.cc/topics…
  • https://developer.android.com…
  • https://android.jlelse.eu/con…

以上就是本文的全部内容,如有谬误,欢送评论区指出。原创不易,如果你喜爱本文,欢送点赞、转发、珍藏三连一下。

正文完
 0