1. 程式人生 > >Android多CPU適配總結

Android多CPU適配總結

這兩天準備重構一個好幾年的老專案,專案中使用了NDK,但是莫名的報ABI的錯誤,最終找出問題是專案中.so檔案放的位置不對。ABI的概念之前也瞭解過,今天總結一下。

理解幾個概念

  • NDK
    即Native Development Kit,原生開發工具集,因此又被Google稱為“NDK”,Android開發中NDK允許使用者使用類似C / C++之類的原生程式碼語言執行部分程式。好處是方便呼叫第三方C/C++開源庫,也方便程式碼的移植。
  • JNI
    即Java Native Interface,從字面意思知道充當一個介面的角色,它提供了若干的API實現了Java和其他語言的通訊(主要是C&C++)。
  • ABI
    即Application Binary Interface,應用程式二進位制介面。不同的Android 手機有不同的CPU,進而支援不同的指令集,CPU和指令集的每一種組合有它自己的ABI(應用二進位制檔案),屬於“執行時,應用的機器碼和系統的互動方式”。
  • API
    即Application Programming Interface,應用程式程式設計介面。面向的是與原始碼層面或者庫函式層面的互動方式,發生在編譯期。

API和ABI的一個重要的區別就是前者是面向的是原始碼,後者站在原始碼的角度面向的是驅動不同的cpu架構或者不同的記憶體分配方式(系統不同或者語言不同有可能記憶體分配方式也不同)的二進位制檔案。所以說一套原始碼下來API相同ABI卻很可能不同,後者相容程度要更為嚴格。
API&JNI&ABI聯絡圖

Android支援的CPU架構(ABI)

  • armeabi
    基於 ARM* v5TE 裝置的庫,使用軟體浮點運算,相容所有ARM裝置,通用性強,速度慢。
  • armeabi-v7a
    基於 ARM* v7 裝置的庫,使用硬體浮點運算,具有高階擴充套件功能(2010年)。
  • arm64-v8
    面向第8代、64位ARM處理器的庫。
  • x86
    面向32位intel處理器(2011年)。(桌上型電腦和平板用的較多)
  • x86_64
    面向64位intel處理器(2014年)。
  • mips
    面向mips架構的處理器。(早期索尼的遊戲機,相比intel,其指令系統計算結構更精簡)
  • mips64
    面向64位mips架構的處理器。

AndroidStudio中使用.so庫

兩種形式配置:
1.和eclipse中工程專案一樣,在libs目錄下新建armeabi等目錄的方式,這種方式不是Androidstudio預設支援的,因此要在gradle中新增指定載入.so庫的目錄:

sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}

2.採用AndroidStudio預設的形式,直接在src/main/下新建jniLibs目錄,將armeabi等目錄放到該目錄下即可。另外同級目錄下仍可新建jni目錄NDK開發用於放置cpp或c檔案,即jni和jniLibs可同級存在。

安裝時相容性檢查

安裝到系統中後,so檔案會被提取在:data/app/com.xxxxxxxx.app-x/lib/目錄下(5.0版本)、/data/app-lib/目錄下(4.2版本),其中armeabi和armeabi-v7a會生成arm目錄,arm64-v8a會生成arm64目錄。

安裝app的時候,如果app使用了so檔案,而不存在適合本機cpu架構的so檔案,則會報錯。例如:在x86模擬器上就必須有x86版本的so資料夾。不然無法安裝成功。

執行時相容性檢查

1.檢查目標目錄下是否存在的so庫檔案。有些時候由於專案中不同ABI資料夾下的.so檔案個數不一致,載入後造成某些CPU架構無法正常在提取的目錄下讀取到相應的.so檔案。
2.檢查存在的so檔案是否符合當前cpu架構。某些ABI資料夾的.so不符合。

載入.so檔案規則

當專案中只提供了armeabi目錄時,armeabi-v7a、arm64-v8a架構的程式會去armeabi裡尋找,當專案中同時也提供了armeabi-v7a、armeabi-v8a目錄,系統就不會再去armeabi裡面尋找了,如果在這兩個目錄中找不到則直接報錯。

常見問題

  • ABI目錄中雖然放置的.so檔案一樣,但某些目錄下放置卻不是最優的.so檔案,或者從低版本架構拷貝的。
  • 使用Android高版本平臺版本編譯的.so檔案執行在低版本的裝置上。
  • 沒有為每個支援的CPU架構提供對應的.so檔案。
  • 將.so檔案放在錯誤的地方。
  • 只提供armeabi架構的.so檔案而忽略其他ABIs的。

以上這些問題要麼導致報錯,要麼導致效能低下,即使能執行但在某些老舊的裝置上也可能發生錯誤。

建議

這裡根據上邊的問題在開發的過程中提幾條建議:
- 如果應用程式想安裝範圍廣一些,那麼儘可能包含所有的ABI,當然這會導致安裝包很大。
- 如果考慮到應用的安裝範圍,比如針對更多的是手機,則可適配armeabi、armeabi-v7、arm64-v8。
- 如果考慮到安裝包的大小,而不太注重CPU效能,可能面對的都是市面上的新機,可僅僅適配armeabi。
- 如果針對多個ABI,則每個ABI目錄下的.so檔案都是一樣的,最好都是針對該ABI的。
- Native Libs Monitor這個應用可以幫助我們理解手機上安裝的APK用到了哪些.so檔案,以及.so檔案來源於哪些函式庫或者框架。
- 某些時候引用多個第三方庫,可能libs資料夾和jniLibs資料夾都放的有,此時會造成衝突,所以儘可能的都放在一個地方。

上邊選擇性的適配ABI可在Gradle中配置:

ndk {
    //選擇要新增的對應cpu型別的.so庫。
    abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
    // 還可以新增 'x86', 'x86_64', 'mips', 'mips64'
}

注意:ndk.abiFilters指定的abi平臺的庫資料夾及庫檔案,要麼都存在,要麼都不存在。

Tap:

  1. 有些時候我們執行安裝在模擬器上的時候報“Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]”

    具體錯誤原因:由於使用了native libraries 。該native libraries 不支援當前的cpu的體系結構。

    INSTALL_FAILED_NO_MATCHING_ABIS is when you are trying to install an app that has native libraries and it doesn’t have a native library for your cpu architecture. For example if you compiled an app for armv7 and are trying to install it on an emulator that uses the Intel architecture instead it will not work.

    簡單點說,雖然工程支援執行在某abi架構(例如模擬器x86架構)機器上,但是工程所包含的庫卻沒有該abi架構所支援的庫,導致無法安裝執行,這在上面闡述很清楚。

    解決這個問題有兩種方式:
    a.將x86的庫資料夾及檔案補全。
    b.移除ndk.abiFilters,採用abi分包,構建apk時單獨分離出支援x86架構的apk。

    splits {
      abi {
        enable true
        reset()
        include 'x86'
        universalApk true
      }
    }
  2. 提到上邊構建apk分包,有多種分包形式,可以按照abi支援進行分,也可以按照螢幕密度支援進行分:

    splits {
      density {
        enable true
        reset()
        include "mdpi", "hdpi"
      }
      abi {
        enable true
        reset()
        include "x86", "x86_64"
      }
    }

參考