Android arm64-v8a、armeabi-v7a、armeabi、x86詳解

最近在用flutter打包的時候,遇到了包打不出來的情況,後面查了半天原因,發現是沒有配置arm導致的,配了之後就打出來了,乘着這個契機,重頭來學習了一下abi

開始之前

開始之前先需要知道lib、libs等知識
一. lib和libs
放在lib中的是被reference的,放在libs中的是被include的。
放在libs中的文件會自動被編輯器所include。所以不要把API放到libs裏去。
lib的內容是不會被打包到APK中,libs中的內容是會被打包進APK中

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

三. .so庫該如何存放
放置 .so 文件的正確姿勢其實就兩句話:
• 爲了減小 apk 體積,只保留 armeabi 和 armeabi-v7a 兩個文件夾,並保證這兩個文件夾中 .so 數量一致
• 對只提供 armeabi 版本的第三方 .so,原樣複製一份到 armeabi-v7a 文件夾
存放so的規則:
你應該儘可能的提供專爲每個ABI優化過的.so文件,但要麼全部支持,要麼都不支持:你不應該混合着使用。你應該爲每個ABI目錄提供對應的.so文件。
關於存放的問題,可以看看這篇
四. libs下armeabi等的作用是什麼
存放.so庫,主要針對不同的設備兼容,也可以說是專門針對不同Android手機下CPU架構的兼容。
下面就來扯一下安卓cpu
Android 設備的CPU類型(通常稱爲”ABIs”)

架構介紹

早期的Android系統幾乎只支持ARMv5的CPU架構,後面發展到支持七種不同的CPU架構:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯着一個相應的ABI。
應用程序二進制接口(Application Binary Interface)定義了二進制文件(尤其是.so文件)如何運行在相應的系統平臺上,從使用的指令集,內存對齊到可用的系統函數庫。在Android 系統上,每一個CPU架構對應一個ABI:armeabi,armeabi-v7a,x86,mips,arm64- v8a,mips64,x86_64。
但是最新的谷歌官方文檔已經把mips和armv5移除了,如圖所示:
在這裏插入圖片描述
各版本分析如下:
• mips / mips64: 極少用於手機可以忽略(谷歌最新的文檔已經不支持了)
• x86 / x86_64: x86 架構的手機都會包含由 Intel 提供的稱爲 Houdini 的指令集動態轉碼工具,實現 對 arm .so 的兼容,再考慮 x86 1% 以下的市場佔有率,x86 相關的兩個 .so 也是可以忽略的
• armeabi: ARM v5 這是相當老舊的一個版本,缺少對浮點數計算的硬件支持,在需要大量計算時有性能瓶頸
• armeabi-v7a: ARM v7
• arm64-v8a: 64位支持,目前主流的版本,雖然網上很多博客都說v7是主流版本,但是我親自試驗了很多手機,都是arm64-v8a的架構,測試機型包括小米5-小米9,華爲P30,華爲mate10,魅藍2等均是v8架構
查詢手機cpu命令行:

adb shell getprop ro.product.cpu.abi

無圖無真相:
在這裏插入圖片描述
只有一款不知名的oppo手機,android系統4.3,用的是v7的架構
在這裏插入圖片描述

ABI是如何工作的

2020.06更新, 看到一篇很好的文章搬過來了,感謝原作者(https://juejin.im/post/5eae6f86e51d454ddb0b3dc6
官方文檔解釋如下:
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-v7a和armeabi,以armeabi-v7a爲主ABI的設備,輔助ABI爲armeabi。
另外,x86 架構的手機都會包含由 Intel 提供的稱爲 Houdini 的指令集動態轉碼工具,實現對 arm .so 的兼容,也就是說有適配armeabi平臺的APP是可以跑在x86手機上的。

ABI具體適配流程

在這裏插入圖片描述
對於一個cpu是arm64-v8a架構的手機,它運行app時,進入jnilibs去讀取庫文件時,先看有沒有arm64-v8a文件夾,如果沒有該文件夾,去找armeabi-v7a文件夾,如果沒有,再去找armeabi文件夾,如果連這個文件夾也沒有,就拋出異常;
如果有arm64-v8a文件夾,那麼就去找特定名稱的.so文件,注意:如果沒有找到想要的.so文件,不會再往下(armeabi-v7a文件夾)找了,而是直接拋出異常。

我們項目中該如何適配呢

Q1: 只適配了armeabi-v7a,那如果APP裝在其他架構的手機上,如arm64-v8a上,會蹦嗎?
A: 不會,但是反過來會。
因爲armeabi-v7a和arm64-v8a會向下兼容:

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

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

優點:基本上適配了全部CPU架構(除了淘汰的mips和mips_64)
缺點:性能低,相當於在絕大多數手機上都是需要輔助ABI或動態轉碼來兼容

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

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

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

對於64位手機跟64位處理器

ARM64位處理器和電腦的64位處理器是兩個截然不容的概念,他並不是64位就能原生向下兼容32位程序,而是通過64位處理器中集成的32位架構來運行32位程序。說得通俗點,它不是以64位形態來運行32位程序,卻是以32位的形態運行32位程序的。

由於目前新出的64位處理器包含兩個架構,而且製程技術沒有提升(28nm),同時在手機與平板上,芯片面積有着嚴格的限定,不能過分增加,這導致64位ARM處理器平均分配到每個架構的晶體管數量銳減,也就是說從64位處理器中的32位架構方面,對於同規格的32位處理器而言,不但沒有提高,性能反而是一定規模下降的。但處理器廠家又必須給消費者一個交代,以更好的推廣64位,所以廠家就必須在其他方面提升性能,以彌補CPU的晶體管數量減少帶來的損失。比如:更換性能更強的GPU、提升內存帶寬、多核心虛擬單顆核心提升單核性能、聯合跑分軟件商修改跑分權重(提升GPU分數,降低CPU分數的權重)等等。這樣,揚長避短,最終到達消費者手裏,用跑分軟件一跑,確實有提升,用戶開心,廠家腰包也鼓了。

綜上所述,ARM64位處理器從嚴格意義來說,叫它ARM32+64更加貼切,他相對於ARM32位處理器,有倒退的地方,也有進步的餘地,但正因爲倒退激起了ARM進取的決心,讓它大刀闊斧的向前變革,不得不說也算一種進步。但ARM64在的手機上真的有用嗎?我只能說,目前確實沒啥用,但今後或許有。(其他地方蒐羅的) 綜上所述,ARM64位處理器從嚴格意義來說,叫它ARM32+64更加貼切,他相對於ARM32位處理器,有倒退的地方,也有進步的餘地,但正因爲倒退激起了ARM進取的決心,讓它大刀闊斧的向前變革,不得不說也算一種進步。但ARM64在的手機上真的有用嗎?我只能說,目前確實沒啥用,但今後或許有。(其他地方蒐羅的)

真正的64位手機並不止單純停留在處理器上,如果只因爲它的處理器是64位,就稱其爲64位手機的話,我們可以毫不猶疑的說這可能是虛假宣傳,好在聯想很聰明,在發佈A678t和A805e宣傳的時候,只說64位處理器手機。
“64位處理器手機”與“64位手機”是兩種天壤之別的概念:只要是處理器包含64架構位的,就可以稱“64位處理器手機”,這種手機也許還運行不了64位程序,只是用來搶佔市場,和32位手機比起來優勢並不明顯。

“64位手機”就不同了:它包含着64位處理器、64位標準系統、64位安卓虛擬機、以及64位程序,這纔是真正意義上的64位手機!
谷歌官方曾說,安卓很早前就支持64位了,這話不假,從Android4.0到Android4.4,安卓系統都支持64位的硬件,但是這僅僅表示底層驅動支持64位,能運行在64位的硬件之上,僅此而已。然而,上層運行軟件的,無論是Dalvik的虛擬機,還是ART虛擬機都是32位的。也就是說,只要你的手機系統是Android4.0—4.4,即便你的處理器是64位,也只能在32位虛擬機下運行32位程序,就算真的64位程序擺在你眼前,也無法安裝。。

但是谷歌官方今年年初就已經發布強制需要64位架構
在這裏插入圖片描述
早在今年(2019)一月份,Google 就發佈通知,在今年 8 月 1 日開始,上架的 App,除了提供 32 位的版本之外,還需要提供 64 位的版本。

因此,項目之前強制只使用armeabi一種架構的方式已經不行了。
那這裏說的 64 位版本支持,到底是什麼?
如果你的應用,完全是使用 Java 或者 Kotlin 編寫代碼,不包含任何原生(Native)的支持,那麼就表示這個應用已經支持 64 位。
但是應用內使用了任何原生(Native)的支持(so 庫),就需要針對這些 so 文件,針對不同的 CPU 架構提供不同的版本的 so 支持。
需要注意的是,有些時候,在我們自身的代碼中,確實沒有用到原生的支持,但是在 App 中使用的一些第三方庫中卻包含了。
此時最穩妥的方式,就是針對最終打包生成的 APK 文件進行分析,來判斷是否需要提供 64 位架構的支持。

打包配置

split分包
這個命令可以按照各種規則去分包,比如按照abi,屏幕密度(即ldpi,hdpi等)分包

splits {
        abi {
            enable true
            reset()
            include 'x86','armabi'
            exclude 'armeabi', 'armeabi-v7a', "arm64-v8a"
            universalApk true
        }
    }

include就是包括,exclude就是不包括。包括的配置每一個項都會生成一個apk包。

但是這樣配置,會生成兩個包,一個只包含x86的so庫,一個只包含armabi的so庫。,顯然不符合需求

ndk{abiFilters:}過濾
這個指令可以配置只打包你配置的so庫,沒有配置的就不打包,很靈活。

第三方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 配置,來避免一些未知的錯誤

//過濾x86的so庫
ndk {
    abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
}

這樣配置會將armeabi,armeabi-v71,arm64-v8a這3個包下的so庫都打包到一個apk,而不像splits會每一個包打一個apk.

參考:
對於android cpu架構 ARM 和x86的區別 按照cpu架構分別打包Apk
Android 中arm64-v8a、armeabi-v7a、armeabi、x86簡介~
https://juejin.im/post/5eae6f86e51d454ddb0b3dc6

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章