目前主流APP是如何適配不同的CPU架構的?

復制復制復制, 記錄一下方便查找

首先,我們下載一些主流的APK,看一下他們的適配情況,這里我分析了微信、手機QQ、支付寶和淘寶這4個APP的適配情況:

image

可以看到,微信適配的是arm64-v8a(微信應該是最近才適配到arm64-v8a,以前是armeabi),支付寶和手Q適配的是armwabi,淘寶適配的是armwabi-v7a。各個APP適配的平臺不太一樣,但是他們有一個共同點,那就是它們只指定了一個平臺。

等等,上面這些APP只適配了一中CPU架構,比如只適配了armwabi-v7a,那如果APP裝在其他架構的手機上,如arm64-v8a上,會蹦嗎?

要弄清楚這個問題,我們得先搞清楚,ABI是如何工作的。

ABI 是如何工作的?

官方文檔解釋如下:

Android 系統(tǒng)在運行時知道它支持哪些 ABI,因為版本特定的系統(tǒng)屬性會指示:

  • 設備的主要 ABI,與系統(tǒng)映像本身使用的機器代碼對應。
  • (可選)與系統(tǒng)映像也支持的其他 ABI 對應的輔助 ABI。

此機制確保系統(tǒng)在安裝時從軟件包提取最佳機器代碼。

為實現最佳性能,應直接針對主要 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-v7a和armeabi,以armeabi-v7a為主ABI的設備,輔助ABI為armeabi。

另外,x86 架構的手機都會包含由 Intel 提供的稱為 Houdini 的指令集動態(tài)轉碼工具,實現對 arm .so 的兼容,也就是說有適配armeabi平臺的APP是可以跑在x86手機上的。

總的來說
因為armeabi-v7a和arm64-v8a會向下兼容:
只適配armeabi的APP可以跑在armeabi,x86,x86_64,armewabi-v7a,arm64-v8上
只適配armeabi-v7a可以運行在armeabi-v7a和arm64-v8a
只適配arm64-v8a 可以運行在arm64-v8a上

主輔助ABI具體適配流程

前面說了ABI的工作原理,一個Android設備支持主輔ABI,那么他們具體是如何工作的呢?我們以arm64-v8a架構的手機為例:

image

對于一個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"http:// 指定ndk需要兼容的ABI(這樣其他依賴包里x86,armeabi,arm-v8之類的so會被過濾掉) 
    }  
}

Android 7種CPU架構在當前市場的占有率

  • arm64-v8a: 目前主流版本

  • armeabi-v7a: 一些老舊的手機

  • x86 / x86_64: x86 架構的手機都會包含由 Intel 提供的稱為 Houdini 的指令集動態(tài)轉碼工具,實現對 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。

image

我們自己的APP中該如何適配?

這里就可以回答前面的兩個問題了。

Q1: 只適配了armwabi-v7a,那如果APP裝在其他架構的手機上,如arm64-v8a上,會蹦嗎?

A: 不會,但是反過來會。

因為armwabi-v7a和arm64-v8a會向下兼容:

  • 只適配armeabi的APP可以跑在armeabi,x86,x86_64,armwabi-v7a,arm64-v8上
  • 只適配armwabi-v7a可以運行在armwabi-v7a和arm64-v8a
  • 只適配arm64-v8a 可以運行在arm64-v8a上

那我們該如何適配呢?給出如下幾個方案:

  • 方案一:只適配armeabi

優(yōu)點:基本上適配了全部CPU架構(除了淘汰的mips和mips_64)

缺點:性能低,相當于在絕大多數手機上都是需要輔助ABI或動態(tài)轉碼來兼容

  • 方案二:只適配 armwabi-v7a

同理方案一,只是又篩掉了一部分老舊設備,在性能和兼容二者中比較平衡

  • 方案三: 只適配 arm64-v8

優(yōu)點: 性能最佳

缺點: 只能運行在arm64-v8上,要放棄部分老舊設備用戶

這三種方案都是可以的,現在的主流APP適配中,這三種都有,大部分是前2種方案。具體選哪一種就看自己的考量了,以性能換兼容就arm64-v8,以兼容換性能armeabi,二者稍微平衡一點的就armwabi-v7a。

目前來說,大多數的主流APP用的都是armeabi或armwabi-v7a,只有像微信這種牛逼的APP,為了追求性能和用戶體驗,放棄了少部分設備,這也說得通吧,畢竟微信也不在乎蒼蠅那點肉。

ABI split-性能+兼容全都要

其實到上一小節(jié),本文就該結束了,但總感覺優(yōu)點意猶未盡,除了適配所有全部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 中就只包含一個平臺,如下:

image
image

這樣,又能保證性能,又能不額外增加APK的大小,同時又又很完美的兼容,因為可以為所有架構都單獨打一個包,一舉多得。

同時,Google Play 支持上傳多個APK:

image

這樣,就能根據不同的CPU架構,下載不同的包啦,但是,很遺憾,國內的應用商店目前還不支持。

注意事項

  • 雖然ABI加載so會向下兼容,但是如果你的App同時存在兩種架構如:"armeabi", "arm64-v8a"。 那么不同Cpu架構的手機便只會加載對應的so庫不再向下兼容。比如a.so存在armeabi中,不存在arm64-v8a中。那么arm64-v8a手機加載a.so庫時,便會崩潰,
    因此需要每個so庫同時添加到每種架構中。

  • armeabi-v7a與armeabi都適用于32位CPU架構,因此理論上他們的So庫時完全通用的,即如果你的項目只適配了armeabi架構,但是第三方框架只提供了armeabi-v7a的So庫,也是可以直接用的。但是arm64-v8a是64位的,所以無法通用。

  • 一些沒人維護的第三方庫,年久失修,可能沒有arm64-v8a架構的So庫,我們這個時候就要考慮是否適配arm64-v8a架構,或者使用其他庫代替。

參考

Android ABI
Controlling APK Size When Using Native Libraries

作者:GameProgramer
鏈接:http://m.itdecent.cn/p/8314778e8dfd
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯系作者獲得授權,非商業(yè)轉載請注明出處。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容