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
armeabiv7a
arm64v8a
x86
x86_64
mips
mips64
Bugtags 的 NDK 庫支援如上所有的 CPU 構架:
但不是所有人的開發者提供的 NDK 庫都支援所有的 CPU 構架:
上面的這個開發者提供的庫,就只支援 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 中,你需要手動控制你的工程中的這個資料夾裡面的內容:
以達到上述的原則,使得在不同的構架的裝置上運轉正常。
參考文獻
相關文章
- Andorid Studio NDK開發-使用NDK庫
- 大前端架構思考與選擇前端架構
- Android NDK開發(二) 使用ndk-build構建工具進行NDK開發AndroidUI
- APP開發實戰170-ABI管理和SO檔案的使用簡介APP
- 安裝APK時SO庫的選擇策略APK
- 如何選擇合適的雲資料庫架構與規格資料庫架構
- Andorid Studio NDK開發:使用庫
- 架構師的選擇(覺悟)架構
- 鴻蒙手機版JNI實戰(JNI開發、SO庫生成、SO庫使用)鴻蒙
- WP7開發中的資料庫系統選擇資料庫
- Gradle 使用技巧(二) - SO/NDK過濾Gradle
- 機器學習研究與開發平臺的選擇機器學習
- 支付寶前端應用架構的發展和選擇前端應用架構
- 事件驅動架構 vs. RESTful架構:通訊模式對比與選擇事件架構REST模式
- Java開發中的執行緒安全選擇與Swing(轉)Java執行緒
- 利用IDEA進行JNI開發:使用NDK生成Linux平臺下的so檔案IdeaLinux
- AndroidStudio中的NDK開發初探Android
- C++中的選擇結構C++
- switch選擇結構使用
- 深圳市教您如何選擇雲資料庫MySql的架構版本?資料庫MySql架構
- 遊戲開發架構中的資料與後設資料遊戲開發架構
- 使用flask開發RESTful架構的api伺服器端(1)–什麼是RESTful和為什麼選擇flaskFlaskREST架構API伺服器
- 順序與選擇結構
- 系統開發中的B/S架構架構
- Android Studio中NDK開發Android
- WEB開發中合理選擇圖片格式Web
- Android NDK開發之旅15 NDK Eclipse下NDK開發流程AndroidEclipse
- Android NDK 更新正式支援 64 位 ABIAndroid
- 順序結構與選擇結構
- Molecule 在構建工具中的選擇
- API架構的選擇,RESTful、GraphQL還是gRPCAPI架構RESTRPC
- 微服務架構到底應該如何選擇?微服務架構
- 分散式架構下如何選擇最佳 Store?分散式架構
- Java企業系統架構選擇考量Java架構
- ndk-build 編譯多個CPU架構的動態連結庫UI編譯架構
- 關於開發工具的選擇
- Android開發中的MVP架構詳解AndroidMVP架構
- 前端-選擇開發工具前端