Android ndk.abiFilters armeabi-v7a、arm64-v8a、arneabi、x86、x86-64 解析


故事背景
今天公司項目打完包,突然發現apk的size從原來的50多M激增到85M,作為敏感的我,肯定是要查清楚原因的。
如何排查呢,肯定是結果出發,我們解壓了最新的apk,對比上個版本的apk,看一下具體增大的文件是哪部分?

 

 

可以清晰地看到,lib文件夾下增加了很多文件。
看了一下這個周期的開發需求,我發現是有一個同學在處理接入一個第三方庫的時候,增加了ndk的abiFilter種類,具體代碼為:

android {
    ......
    defaultConfig {
           ......
        ndk {
            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }
    }


那么在此之前我確實對這部分內容沒有很透徹的研究,借着此機會好好研究了一下。

so庫
NDK編譯出來的動態鏈接庫。
一些重要的加密算法或者核心協議一般都用c寫然后給java調用。這樣可以避免反編譯后查看到應用的源碼。

放置so文件的正確方式:
• 為了減小 apk 體積,只保留 armeabi 和 armeabi-v7a 兩個文件夾,並保證這兩個文件夾中 .so 數量一致
• 對只提供 armeabi 版本的第三方 .so,原樣復制一份到 armeabi-v7a 文件夾
原則:你應該盡可能的提供專為每個ABI優化過的.so文件,但要么全部支持,要么都不支持:你不應該混合着使用。你應該為每個ABI目錄提供對應的.so文件。

ABI是啥
ABI是英文Application Binary Interface的縮寫,也就是應用二進制接口。

不同Android設備,使用的CPU架構可能不同,因此支持不同的指令集。 CPU 與指令集的每種組合都有其自己的應用二進制界面(或 ABI),ABI非常精確地定義了應用程序的機器代碼應如何在運行時與系統交互。您必須為要與您的應用程序一起使用的每種CPU架構指定一個ABI(Application Binary Interface)。

ABI 包含以下信息:

可使用的 CPU 指令集(和擴展指令集)。
運行時內存存儲和加載的字節順序。Android 始終是 little-endian。
在應用和系統之間傳遞數據的規范(包括對齊限制),以及系統調用函數時如何使用堆棧和寄存器。
可執行二進制文件(例如程序和共享庫)的格式,以及它們支持的內容類型。Android 始終使用 ELF。
如何重整 C++ 名稱。
總而言之,abi就是幫助適配不同的CPU架構的。

ABI現狀及原理分析
Android 目前共支持7種CPU架構:
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
市場占有率上,x86 / x86_64/armeabi/mips / mips6 的架構,基本可以不不考慮了,它們的占有量應很少很少了,arm64-v8a作為最新一代架構,應該是目前的主流,armeabi-v7a只存在少部分老舊手機。

難道我要全部寫上?if so,我們的apk size豈不是無比巨大。

根據參考資料:微信適配的是arm64-v8a(部分渠道下載的apk可能適配的是armeabi-v7a,不做過多糾結,有可能采用的是動態下發的方案,具體不詳,以前是armeabi),支付寶和手Q適配的是armeabi,淘寶適配的是armeabi-v7a。各個APP適配的平台不太一樣,但是他們有一個共同點,那就是它們只指定了一個平台。

看到這里,有的同學就開始問了,上面這些APP只適配了一中CPU架構,比如只適配了armeabi-v7a,那如果APP裝在其他架構的手機上,如arm64-v8a上,會crash嗎?

實際上,一個Android設備可以支持多種ABI,設備主ABI和輔助ABI,以arm64-v8a為主ABI的設備,輔助ABI為armeabi-v7a和armeabi;以armeabi-v7a為主ABI的設備,輔助ABI為armeabi。
另外,x86 架構的手機都會包含由 Intel 提供的稱為 Houdini 的指令集動態轉碼工具,實現對 arm .so 的兼容,也就是說有適配armeabi平台的APP是可以跑在x86手機上的。

下面以arm64-v8a架構的手機為例:

特別需要注意的情況是在命中了文件夾,而未命中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的APP可以跑在armeabi,x86,x86_64,armeabi-v7a,arm64-v8上
只適配armeabi-v7a可以運行在armeabi-v7a和arm64-v8a
只適配arm64-v8a 可以運行在arm64-v8a上
因此我們有如下適配方案:

只適配armeabi:
優點:基本可以適配所有手機機型,除了淘汰的mips和mips_64
缺點:在大多數手機上都需要利用輔助ABI或者動態轉碼來兼容,性能較差

只適配 armeabi-v7a
只是又篩掉了一部分老舊設備,在性能和兼容二者中比較平衡

只適配 arm64-v8
優點:性能最佳(微信大哥采用的)
缺點:只能運行在arm64-v8上,要放棄部分老舊設備用戶

上述方案只是我們給出的建議,具體還要看實際的需求和考量:以性能換兼容就arm64-v8,以兼容換性能armeabi,二者稍微平衡一點的就armeabi-v7a。

能否做到性能+兼容都要
上述方案多多少少有一些取舍的,有沒有完美的方案,既不放棄性能,也能完全兼容。答案是肯定的,Google已經安排上了,通過abi split,分包,為每一個CUP架構打一個apk,該apk 中就只包含一個平台。 google play支持上傳多個apk,但是,據我所知,目前國內的應用商店是不支持的。

 

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

 

本文大量參考如下文章,如有侵權,請聯系本人刪除。
參考文章:https://juejin.cn/post/6844904148589084680

https://blog.csdn.net/yearningseeker/article/details/118554430

https://blog.csdn.net/weixin_42600398/article/details/118601134


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM