文章首發於公衆號「 技術最TOP 」
,天天都有乾貨文章持續更新,能夠微信搜索「 技術最TOP 」
第一時間閱讀!
前幾天啊,在公衆號發了一篇文章《優化ApK大小之ABI Filters 和 APK split》,評論區收到了一些留言說,文章講得不夠深刻,關於系統是如何選擇不一樣abi下的so庫的?當前APP該如何適配?該去掉哪些該保留哪些?都存在一些疑問。android
所以,決定親自更文一篇,系統地講一下關於Android CPU架構方面的一些東西,以及結合大廠APP如微信、支付寶、淘寶等APP的適配狀況,分析咱們開發APP中該如何適配。本文涉及如下幾個問題:微信
本篇文章中,就一一爲你解答這些疑問。架構
ABI是英文Application Binary Interface的縮寫,及應用二進制接口。app
不一樣Android設備,使用的CPU架構可能不一樣,所以支持不一樣的指令集。 CPU 與指令集的每種組合都有其本身的應用二進制界面(或 ABI),ABI很是精確地定義了應用程序的機器代碼應如何在運行時與系統交互。您必須爲要與您的應用程序一塊兒使用的每種CPU架構指定一個ABI(Application Binary Interface)。ide
ABI 包含如下信息:函數
如何重整 C++ 名稱。工具
Android目前支持如下7種ABIs:性能
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
當咱們想要在項目中使用 native(C/C++) 類庫,咱們必須對要支持的處理器架構提供對應編譯包。每一個處理器架構須要咱們提供一個或多個包含native代碼的.so文件。gradle
默認狀況下,爲了使APP有更好的兼容性,咱們使用Android Studio 或者命令打包時,會默認支持全部的架構,但相應的APK size 會瘋狂的增大。對於用戶來講,目標設備只須要其中一個版本,但當用戶下載APK時,會所有下載(對用戶來講至關的不友好)。優化
怎麼辦呢?abifilters
爲咱們提供瞭解決方案,abifilters
爲咱們提供了選擇適配指定CPU架構的能力,只須要在app下的build.gradle
添加以下配置:
android { defaultConfig { ndk { abiFilters 'arm64-v8a', 'x86_64' } } }
你可能看了上面的這些文字,還不能理解abi的做用,那麼咱們就用一個簡單的例子來講明一下。
首先,咱們建立一個最簡單的Hello world
應用,只有一個Activity和一個啓動圖標。咱們看如下打出來的apk:
沒有任何的原生庫使用,大小爲2.1MB
,如今咱們爲它添加多ABI原生庫支持,咱們在項目中集成Realm
,而後打包。
看到沒,apk大小從2.1MB
猛增長到11.2MB
,多了一個原生so庫的文件夾,大小爲8.8MB
,咱們來看一下它的詳細信息:
如上圖所示,Realm
爲5種CPU架構生成了.so
庫,分別是mips
、x86
、x86_64
、arm64-v8a
、armeabi-v7a
。增長了8.8MB
包的大小。可是這不是咱們想要的,咱們只想要適配咱們指定的的CPU架構,所以,咱們須要在gralde.build中添加abifilters
配置來完成咱們想要的效果:
android { 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' } } }
效果以下:
能夠看到,只生成了咱們指定CPU架構的so文件,包的大小也減小了5.3MB
。
這時候,你可能會有一個疑問,Android 共支持7種CPU架構,那麼,咱們在實際項目中該適配哪些CPU架構能保證最好的兼容,同時又最大限度的減小APK的大小?
在回答這個問題以前,咱們不妨看一下這些頂級巨頭公司,他們是是如何適配的。
首先,咱們下載一些大廠的APK,看一下他們的適配狀況,這裏我分析了微信、手機QQ、支付寶和淘寶這4個APP的適配狀況:
能夠看到,微信適配的是arm64-v8a
(微信應該是最近才適配到arm64-v8a
,之前是armeabi
),支付寶和手Q適配的是armwabi
,淘寶適配的是armwabi-v7a
。各個APP適配的平臺不太同樣,可是他們有一個共同點,那就是它們只指定了一個平臺。
等等,上面這些APP只適配了一中CPU架構,好比只適配了armwabi-v7a
,那若是APP裝在其餘架構的手機上,如arm64-v8a
上,會蹦嗎?
要弄清楚這個問題,咱們得先搞清楚,ABI是如何工做的。
官方文檔解釋以下:
Android 系統在運行時知道它支持哪些 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的工做原理,一個Android設備支持主輔ABI,那麼他們具體是如何工做的呢?咱們以arm64-v8a
架構的手機爲例:
對於一個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文件夾)找了,而是直接拋出異常。所以,咱們須要在咱們的app中配置 abiFilter 配置,來避免一些未知的錯誤。
defaultConfig { ndk { abiFilters "armeabi"// 指定ndk須要兼容的ABI(這樣其餘依賴包裏x86,armeabi,arm-v8之類的so會被過濾掉) } }
arm64-v8a
: 目前主流版本armeabi-v7a
: 一些老舊的手機x86 / x86_64
: x86 架構的手機都會包含由 Intel 提供的稱爲 Houdini 的指令集動態轉碼工具,實現對 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
。
這裏就能夠回答前面的兩個問題了。
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
優勢:
基本上適配了所有CPU架構(除了淘汰的mips和mips_64)缺點:
性能低,至關於在絕大多數手機上都是須要輔助ABI或動態轉碼來兼容方案二
:只適配 armwabi-v7a
同理方案一,只是又篩掉了一部分老舊設備,在性能和兼容兩者中比較平衡
方案三:
只適配 arm64-v8
優勢:
性能最佳缺點:
只能運行在arm64-v8
上,要放棄部分老舊設備用戶這三種方案都是能夠的,如今的大廠APP適配中,這三種都有,大部分是前2種方案。具體選哪種就看本身的考量了,以性能換兼容就arm64-v8
,以兼容換性能armeabi
,兩者稍微平衡一點的就armwabi-v7a
。
目前來講,大多數的大廠APP用的都是armeabi
或armwabi-v7a
,只有像微信這種牛逼的APP,爲了追求性能和用戶體驗,放棄了少部分設備,這也說得通吧,畢竟微信也不在意蒼蠅那點肉。
其實到上一小節,本文就該結束了,但總感受優勢意猶未盡,除了適配全部所有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 中就只包含一個平臺,以下:
這樣,又能保證性能,又能不額外增長APK的大小,同時又又很完美的兼容,由於能夠爲全部架構都單獨打一個包,一舉多得。
同時,Google Play 支持上傳多個APK:
這樣,就能根據不一樣的CPU架構,下載不一樣的包啦!
可是,可是,可是,很遺憾,國內的應用商店目前還不支持!
參考文章
以上就是本文的所有內容,若有錯誤,歡迎評論區指出。原創不易,若是你喜歡本文,歡迎點贊、轉發、收藏三連一下。