NDK SO 庫開發與使用中的 ABI 構架選擇


Bugtags V1.2.7 引入了 NDK SO 庫,在集成的時候,遇到不同的 SO 庫打包到 APK 時,安裝在某些機器上,出現 java.lang.UnsatisfiedLinkError 加載失敗。

為此,深究了一下原理,和給出了解決方案。

原理

Android 系統本質是一個經過改造的 Linux 系統。最早,Android 系統只支持 ARMv5 的 CPU 構架,隨着 Android 系統的發展,又加入了 ARMv7 (2010), x86 (2011), MIPS (2012), ARMv8, MIPS64 和 x86_64 (2014)。

每一種 CPU 構架,都定義了一種 ABI(Application Binary Interface),ABI 決定了二進制文件如何與系統進行交互。

一般情況下,你不需要關注這些。當你的 APP 中用到了些包含 SO 庫第三方庫,或者自己使用 NDK 來實現了某些功能,你就需要認真閱讀接下來的教程。

NDK SO 支持不同的 CPU 構架

在使用 NDK 開發包含 c/c++ 代碼的 SO 庫的時候,你可以選擇輸出支持如下 ABI CPU 構架:

armeabi
armeabi­v7a
arm64­v8a
x86
x86_64
mips
mips64

Bugtags 的 NDK 庫支持如上所有的 CPU 構架:

bugtags-ndk

但不是所有人的開發者提供的 NDK 庫都支持所有的 CPU 構架:

other-ndk

上面的這個開發者提供的庫,就只支持 armeabi。

其實一般情況下,是沒有問題的,x86 的設備,也會兼容 armeabi 的 SO 庫。

合並打包到 APK 中

如果不做任何設置,Android 的構建系統會把這些來自不同開發者的 SO 庫都合並在一起,打進 APK 壓縮包中。

├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│       └── libBugtags.so
├── res

系統安裝 APK

根據官方 ndk-abi 文檔, Android 系統在安裝一個 APK 的時候,會考慮當前的設備的 CPU 構架和配置(稱為所謂的 primary-abi 和 secondary-abi),去該 APK 文件的對應文件夾去尋找 SO 庫。

假設當前設備是 x86 機器,會優先去 lib/x86 文件夾下尋找 SO 庫:

lib/<primary-abi>/lib<name>.so

如果找不到,同時定義了 secondary-abi,則去如下文件夾尋找:

lib/<secondary-abi>/lib<name>.so

如果找到了,就將文件拷貝到 APK 的安裝目錄的如下文件夾中:

 /lib/lib<name>.so

找不到對應的 SO,安裝正常,但是當這個 SO 在運行時被使用時,會崩潰。

問題來了

可能你已經發現問題了,當一個 APK 是這種情況:

├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│       └── libBugtags.so
├── res

同時 APK 被安裝到一個 x86 的設備上的時候,以上的尋找過程,將會失敗,運行時,將出現如下報錯:

D/xxx   (10674): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx-2/base.apk"],nativeLibraryDirectories=[/data/app/xxx-2/lib/x86, /vendor/lib, /system/lib]]] couldn't find "libirdna_sdk.so"
D/xxx   (10674):     at java.lang.Runtime.loadLibrary(Runtime.java:366)

此處,筆者有點費解,既然在 x86 文件夾中找不到,應該去 armeabi 文件夾中自動尋找啊,此處留一個 TODO,需要接下來去確認是否是某些機器的原因。

解決方案

准則

NDK SO 開發者應該遵循一個准則:支持所有的平台,否則將會搞砸你的用戶。

NDK SO 使用者應該遵循一個准則:要么支持所有平台,要么都不支持。

然而,事與願違,因為種種原因(遺留 SO、芯片市場占有率、APK 包大小等),並不是所有人都遵循這樣的原則。

折中方案

Android Studio

  • Android Gradle 插件中,可以使用如下方式對 abi 進行過濾:
android {
    ...

    defaultConfig {    
        ...
        ndk {
            // 設置支持的 SO 庫構架,注意這里要根據你的實際情況來設置
            abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
        }
    }

}

關鍵行:

abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'

根據你的 APP 中使用的 SO 庫所支持的構架具體情況,你可以進行具體設置。最終輸出的 apk 中,將會包含你所選擇的 abi。

像前面舉出的例子,就應該只允許 armeabi。

  • 如果在添加 “abiFilter” 之后 Android Studio 出現以下提示:
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin

則在項目根目錄的 gradle.properties 文件中添加:

    android.useDeprecatedNdk=true

Eclipse

Eclipse 中,你需要手動控制你的工程中的這個文件夾里面的內容:

eclipse-libs

以達到上述的原則,使得在不同的構架的設備上運轉正常。

參考文獻

What you should know about .so files

關於Android的.so文件你所需要知道的)

ABI Management


免責聲明!

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



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