Android 關(guān)于CPU類(lèi)型的so文件兼容問(wèn)題(ABI)

當(dāng)我們想要在項(xiàng)目中使用native(C/C++)類(lèi)庫(kù)或者依賴(lài)一些第三方庫(kù)的時(shí)候,往往需要導(dǎo)入包含native代碼的.so文件,默認(rèn)情況下,為了使APP有更好的兼容性,我們使用Android Studio 或者命令打包時(shí),會(huì)默認(rèn)支持所有的架構(gòu),但相應(yīng)的APK size 會(huì)瘋狂的增大。APK文件的大小,也是會(huì)直接影響用戶的安裝率,不利于app的推廣的,為了解決這個(gè)問(wèn)題,Google為我們提供了abifilters,abifilters為我們提供了選擇適配指定CPU架構(gòu)的能力,只需要在app下的build.gradle添加如下配置:

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

ABI

ABI的英文縮寫(xiě)是Application Binary Interface,即應(yīng)用二進(jìn)制接口。不同Android設(shè)備,使用的CPU架構(gòu)可能不同,因此支持不同的指令集。CPU 與指令集的每種組合都有其自己的ABI,ABI非常精確地定義了應(yīng)用程序的機(jī)器代碼應(yīng)如何在運(yùn)行時(shí)與系統(tǒng)交互。

ABI的作用

以最簡(jiǎn)單的“Hello World”應(yīng)用為例,我們看他的apk文件:

640.jpg

沒(méi)有任何的原生庫(kù)使用,大小為2.1MB,現(xiàn)在我們?yōu)樗砑佣郃BI原生庫(kù)支持,我們?cè)陧?xiàng)目中集成Realm,然后打包。

640.png

apk大小從2.1MB增加到了11.2MB,多了一個(gè)原生so庫(kù)的文件夾,大小為8.8MB,如圖所示,Realm為5種CPU架構(gòu)生成了.so庫(kù),分別是mips、x86、x86_64、arm64-v8a、armeabi-v7a。增加了8.8MB包的大小。實(shí)際上用戶使用的時(shí)候,只需要一種so庫(kù)版本,我們只需要適配我們指定的的CPU架構(gòu),因此,我們需要在gralde.build中添加abifilters配置來(lái)達(dá)到我們想要的效果:

    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架構(gòu)
        ndk {
            abiFilters 'arm64-v8a', 'x86_64'
        }
    }
}

效果如下:

640 (1).png

可以看到,只生成了我們指定CPU架構(gòu)的so文件,包的大小也減少了5.3MB。

Android CPU架構(gòu)在當(dāng)前市場(chǎng)的占有率

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

  • arm64-v8a: 第8代、64位ARM處理器,目前主流版本。
  • armeabi-v7a: 第7代及以上的 ARM 處理器,2011年15月以后的生產(chǎn)的大部分Android設(shè)備,現(xiàn)在以arm64-v8a為多。
  • armeabi: 第5代、第6代的ARM處理器,早期的手機(jī)用的比較多,可以兼容所有ARM設(shè)備,速度比較慢。
  • x86 / x86_64: 平板、模擬器用得比較多,x86 架構(gòu)的手機(jī)都會(huì)包含由 Intel 提供的稱(chēng)為 Houdini 的指令集動(dòng)態(tài)轉(zhuǎn)碼工具,實(shí)現(xiàn)對(duì) arm .so 的兼容,而且目前 x86市場(chǎng)占有率很低,可能只有1%,所以x86相關(guān)的兩個(gè)so文件是可以忽略的。
  • mips / mips64: NDK 以前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已不再支持,極少用于手機(jī),可以忽略。

目前手機(jī)市場(chǎng)上,x86 / x86_64/armeabi/mips / mips6 的架構(gòu),基本可以不不考慮了,它們的占有量應(yīng)很少很少了,arm64-v8a作為最新一代架構(gòu),應(yīng)該是目前的主流,armeabi-v7a只存在少部分老舊手機(jī)。

Google Play 從2019年8月開(kāi)始,就強(qiáng)制APP適配arm64-v8a,以慢慢淘汰32位的armeabi-v7a。

640 (2).jpg

查看手機(jī)的CPU ABI

通過(guò) adb 命令查看

  1. 連接手機(jī)到電腦上
  2. 打開(kāi) cmd 命令窗口,輸入命令 adb shell
  3. 然后輸入命令 cat /proc/cpuinfo

通過(guò)代碼獲取

Build.CPU_ABI、Build.CPU_ABI2,API level 大于等于21時(shí),使用Build.SUPPORTED_ABIS

如何適配

ABI是如何工作

一般來(lái)說(shuō),一個(gè)Android設(shè)備可以支持多種ABI,設(shè)備主ABI和輔助ABI,以arm64-v8a為主ABI的設(shè)備,輔助ABI為armeabi-v7a和armeabi,以armeabi-v7a為主ABI的設(shè)備,輔助ABI為armeabi,也就是說(shuō)他是向下兼容的,即arm64-v8a>armeabi-v7a>armeabi

例如:對(duì)于一個(gè)cpu是arm64-v8a架構(gòu)的手機(jī),它運(yùn)行app時(shí),進(jìn)入jnilibs去讀取庫(kù)文件時(shí),先看有沒(méi)有arm64-v8a文件夾,如果沒(méi)有該文件夾,去找armeabi-v7a文件夾,如果沒(méi)有,再去找armeabi文件夾,如果連這個(gè)文件夾也沒(méi)有,就拋出異常;

如果有arm64-v8a文件夾,那么就去找特定名稱(chēng)的.so文件,注意:如果沒(méi)有找到想要的.so文件,不會(huì)再往下(armeabi-v7a文件夾)找了,而是直接拋出異常。

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

特別需要注意的情況是在命中了文件夾,而未命中so文件這種情況:

  • 比如命中了arm64-v8a文件夾,沒(méi)有找到需要的so文件,就不會(huì)再往下(armeabi-v7a文件夾)找了,而是直接拋出異常。
  • 如果你的項(xiàng)目用到了第三方依賴(lài),如果只保留一個(gè)ABI的時(shí)候,建議在Build中加入ndk.abiFilters

例如:第三方aar文件,如果這個(gè)sdk對(duì)abi的支持比較全,可能會(huì)包含armeabi、armeabi-v7a、x86、arm64-v8a、x86_64五種abi,而你應(yīng)用的其它so只支持armeabi、armeabi-v7a、x86三種,直接引用sdk的aar,會(huì)自動(dòng)編譯出支持5種abi的包。但是應(yīng)用的其它so缺少對(duì)其它兩種abi的支持,那么如果應(yīng)用運(yùn)行于arm64-v8a、x86_64為首選abi的設(shè)備上時(shí),就會(huì)crash了。

因此,我們需要在我們的app中配置 abiFilter 配置,來(lái)避免一些未知的錯(cuò)誤。

defaultConfig {
    ndk {
        abiFilters "armeabi"http:// 指定ndk需要兼容的ABI(這樣其他依賴(lài)包里x86,armeabi,arm-v8之類(lèi)的so會(huì)被過(guò)濾掉)
    }
}

如何去設(shè)置、去適配

根據(jù)ABI向下兼容性的特點(diǎn),我們可以得出一下這些結(jié)論:

因?yàn)閍rmeabi-v7a和arm64-v8a會(huì)向下兼容:

  • 只適配armeabi的APP可以跑在armeabi,x86,x86_64,armewabi-v7a,arm64-v8上
  • 只適配armeabi-v7a可以運(yùn)行在armeabi-v7a和arm64-v8a
  • 只適配arm64-v8a 可以運(yùn)行在arm64-v8a上
    ye
    那我們?cè)撊绾芜m配呢?

一、只適配armeabi

  • 優(yōu)點(diǎn):基本上適配了全部CPU架構(gòu)(除了淘汰的mips和mips_64)
  • 缺點(diǎn):性能低,相當(dāng)于在絕大多數(shù)手機(jī)上都是需要輔助ABI或動(dòng)態(tài)轉(zhuǎn)碼來(lái)兼容

二、只適配 armeabi-v7a

能運(yùn)行在arm64-v8和armeabi-v7a機(jī)器上,在性能和兼容二者中比較平衡

三、只適配 arm64-v8

只能運(yùn)行在arm64-v8上,要放棄部分老舊設(shè)備用戶,優(yōu)點(diǎn)就是:性能最佳

這三種方案都是可以的,現(xiàn)在的大廠APP適配中,這三種都有,大部分是前2種方案。
具體選哪一種就看自己的考量了,以性能換兼容就arm64-v8,以兼容換性能armeabi,二者稍微平衡一點(diǎn)的就armeabi-v7a。
最后,根據(jù)市場(chǎng)上ABI的占有率情況,我們可以選擇第二種方案,性能和兼容二者中比較平衡,一則armeabi類(lèi)型的手機(jī)已經(jīng)很少了,二則可以兼容armeabi-v7a;若你比較看重性能的也可以選擇第一種方案。

abi split分包

abi split,分包就是為每個(gè)CPU架構(gòu)單獨(dú)打一個(gè)APK,為了性能和兼容同時(shí)兼得,Google提供了abi split分包的機(jī)制:

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

然后,就能為每個(gè)CPU架構(gòu)單獨(dú)打一個(gè)APK,該apk 中就只包含一個(gè)平臺(tái),如下:

640 (1).png

這樣,又能保證性能,又能不額外增加APK的大小,同時(shí)又又很完美的兼容,因?yàn)榭梢詾樗屑軜?gòu)都單獨(dú)打一個(gè)包,一舉多得。

同時(shí),Google Play 支持上傳多個(gè)APK:

640 (2).png

這樣,就能根據(jù)不同的CPU架構(gòu),下載不同的包啦!

注意:但是,很遺憾,國(guó)內(nèi)的應(yīng)用商店目前還不支持這種方式哦?。。?!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容