Android 关于CPU类型的so文件兼容问题(ABI)

当我们想要在项目中使用native(C/C++)类库或者依赖一些第三方库的时候,往往需要导入包含native代码的.so文件,默认情况下,为了使APP有更好的兼容性,我们使用Android Studio 或者命令打包时,会默认支持所有的架构,但相应的APK size 会疯狂的增大。APK文件的大小,也是会直接影响用户的安装率,不利于app的推广的,为了解决这个问题,Google为我们提供了abifilters,abifilters为我们提供了选择适配指定CPU架构的能力,只需要在app下的build.gradle添加如下配置:

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

ABI

ABI的英文缩写是Application Binary Interface,即应用二进制接口。不同Android设备,使用的CPU架构可能不同,因此支持不同的指令集。CPU 与指令集的每种组合都有其自己的ABI,ABI非常精确地定义了应用程序的机器代码应如何在运行时与系统交互。

ABI的作用

以最简单的“Hello World”应用为例,我们看他的apk文件:

640.jpg

没有任何的原生库使用,大小为2.1MB,现在我们为它添加多ABI原生库支持,我们在项目中集成Realm,然后打包。

640.png

apk大小从2.1MB增加到了11.2MB,多了一个原生so库的文件夹,大小为8.8MB,如图所示,Realm为5种CPU架构生成了.so库,分别是mips、x86、x86_64、arm64-v8a、armeabi-v7a。增加了8.8MB包的大小。实际上用户使用的时候,只需要一种so库版本,我们只需要适配我们指定的的CPU架构,因此,我们需要在gralde.build中添加abifilters配置来达到我们想要的效果:

    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'
        }
    }
}

效果如下:

640 (1).png

可以看到,只生成了我们指定CPU架构的so文件,包的大小也减少了5.3MB。

Android CPU架构在当前市场的占有率

Android目前支持7种ABIs:mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a

  • arm64-v8a: 第8代、64位ARM处理器,目前主流版本。
  • armeabi-v7a: 第7代及以上的 ARM 处理器,2011年15月以后的生产的大部分Android设备,现在以arm64-v8a为多。
  • armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多,可以兼容所有ARM设备,速度比较慢。
  • x86 / x86_64: 平板、模拟器用得比较多,x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对 arm .so 的兼容,而且目前 x86市场占有率很低,可能只有1%,所以x86相关的两个so文件是可以忽略的。
  • mips / mips64: NDK 以前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已不再支持,极少用于手机,可以忽略。

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

Google Play 从2019年8月开始,就强制APP适配arm64-v8a,以慢慢淘汰32位的armeabi-v7a。

640 (2).jpg

查看手机的CPU ABI

通过 adb 命令查看

  1. 连接手机到电脑上
  2. 打开 cmd 命令窗口,输入命令 adb shell
  3. 然后输入命令 cat /proc/cpuinfo

通过代码获取

Build.CPU_ABI、Build.CPU_ABI2,API level 大于等于21时,使用Build.SUPPORTED_ABIS

如何适配

ABI是如何工作

一般来说,一个Android设备可以支持多种ABI,设备主ABI和辅助ABI,以arm64-v8a为主ABI的设备,辅助ABI为armeabi-v7a和armeabi,以armeabi-v7a为主ABI的设备,辅助ABI为armeabi,也就是说他是向下兼容的,即arm64-v8a>armeabi-v7a>armeabi

例如:对于一个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会被过滤掉)
    }
}

如何去设置、去适配

根据ABI向下兼容性的特点,我们可以得出一下这些结论:

因为armeabi-v7a和arm64-v8a会向下兼容:

  • 只适配armeabi的APP可以跑在armeabi,x86,x86_64,armewabi-v7a,arm64-v8上
  • 只适配armeabi-v7a可以运行在armeabi-v7a和arm64-v8a
  • 只适配arm64-v8a 可以运行在arm64-v8a上
    ye
    那我们该如何适配呢?

一、只适配armeabi

  • 优点:基本上适配了全部CPU架构(除了淘汰的mips和mips_64)
  • 缺点:性能低,相当于在绝大多数手机上都是需要辅助ABI或动态转码来兼容

二、只适配 armeabi-v7a

能运行在arm64-v8和armeabi-v7a机器上,在性能和兼容二者中比较平衡

三、只适配 arm64-v8

只能运行在arm64-v8上,要放弃部分老旧设备用户,优点就是:性能最佳

这三种方案都是可以的,现在的大厂APP适配中,这三种都有,大部分是前2种方案。
具体选哪一种就看自己的考量了,以性能换兼容就arm64-v8,以兼容换性能armeabi,二者稍微平衡一点的就armeabi-v7a。
最后,根据市场上ABI的占有率情况,我们可以选择第二种方案,性能和兼容二者中比较平衡,一则armeabi类型的手机已经很少了,二则可以兼容armeabi-v7a;若你比较看重性能的也可以选择第一种方案。

abi split分包

abi split,分包就是为每个CPU架构单独打一个APK,为了性能和兼容同时兼得,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 中就只包含一个平台,如下:

640 (1).png

这样,又能保证性能,又能不额外增加APK的大小,同时又又很完美的兼容,因为可以为所有架构都单独打一个包,一举多得。

同时,Google Play 支持上传多个APK:

640 (2).png

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

注意:但是,很遗憾,国内的应用商店目前还不支持这种方式哦!!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容