Android so文件
本篇簡單介紹Android
中so文件相關事項。
CPU架構
目前主流的CPU架構:x86,ARM,MIPS
它們采用的指令集又分為CISC(復雜指令集)和RISC(精簡指令集)兩種
CISC(復雜指令集)
:
1.指令系統龐大,指令功能復雜,指令格式、尋址方式多
2.絕大多數指令需多個機器周期完成
3.各種指令都可訪問存儲器
4.采用微程序控制
5.有專用寄存器,少量
6.難以用優化編譯技術生成高效的目標代碼程序
RISC(精簡指令集)
:
1.統一指令編碼,可快速解譯;
2.泛用的暫存器,所有暫存器可用於所有內容,以及編譯器設計的單純化
3.單純的尋址模式
4.硬件中支援少數資料型別
x86``CISC
ARM``RISC
廣泛應用在嵌入式系統MIPS``RISC
廣泛被使用在許多電子產品、網絡設備、個人娛樂裝置與商業裝置上
CPU架構和ABI
Android
系統目前支持以下七種不同的CPU架構:ARMv5
,ARMv7
(從2010年起),x86
(從2011年起),MIPS
(從2012年起),ARMv8
,MIPS64
和x86_64
(從2014年起),每一種都關聯著一個相應的應用程序二進制接口ABI(Application Binary Interface)。
ABI定義了二進制文件(尤其是.so文件)如何運行在相應的系統平臺上,從使用的指令集,內存對齊到可用的系統函數庫。
ABI\CPU | armeabi | armeabi-v7a | arm64-v8a | mips |
---|---|---|---|---|
ARMv5 | 支持 | |||
ARMv7 | 支持 | 支持 | ||
ARMv8 | 支持 | 支持 | 支持 | |
MIPS | 支持 | |||
MIPS64 | 支持 | |||
x86 | 支持 | 支持 | ||
x86_64 | 支持 |
ABI官方介紹
so文件
so機制讓開發者最大化利用已有的C和C++代碼,達到重用的效果,利用軟件世界積累了幾十年的優秀代碼
so是二進制,沒有解釋編譯的開消,用so實現的功能比純java實現的功能要快
so內存分配不受Dalivik/ART的單個應用限制,減少OOM
相對於java代碼,二進制代碼的反編譯難度更大,一些核心代碼可以考慮放在so中。
so文件的加載
so文件的加載,Android在System類中提供兩種方法。
/**
* See {@link Runtime#loadLibrary}.
*/
public static void loadLibrary(String libName) {
Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}
/**
* See {@link Runtime#load}.
*/
public static void load(String pathName) {
Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
}
System.loadLibrary
這是我們最常用的一個方法,System.loadLibrary只需要傳入so在Android.mk中定義的LOCAL_MODULE的值即可,系統會調用System.mapLibraryName把這個libName轉化成對應平臺的so的全稱並去嘗試尋找這個so加載。比如我們的so文件全名為libmath.so,加載該動態庫只需要傳入math即可:
System.loadLibrary("math");
System.load
官方介紹:
Loads a code file with the specified filename from the local file system as a dynamic library.The filename argument must be a complete path name.
所以它為動態加載非apk打包期間內置的so文件提供了可能,也就是說可以使用這個方法來指定我們要加載的so文件的路徑來動態的加載so文件。比如我們在打包期間並不打包so文件,而是在應用運行時將當前設備適用的so文件從服務器上下載下來,放在/data/data/
System.load("/data/data/<package-name>/mydir/libmath.so");
即可成功加載這個so,開始調用本地方法了。
其實loadLibrary和load最終都會調用nativeLoad(name, loader, ldLibraryPath)方法,只是因為loadLibrary的參數傳入的僅僅是so的文件名,所以,loadLibrary需要首先找到這個文件的路徑,然後加載這個so文件。
而load傳入的參數是一個文件路徑,所以它不需要去尋找這個文件路徑,而是直接通過這個路徑來加載so文件。
但是當我們把需要加載的so文件放在SdCard中,會發生什麽呢?把上面so的路徑改成/mnt/sdcard/libmath.so,再嘗試加載時,會得到如下錯誤:
java.lang.UnsatisfiedLinkError: dlopen failed: couldn‘t map "/mnt/sdcard/libmath.so" segment 1: Permission denied
這是因為SD卡等外部存儲路徑是一種可拆卸的(mounted)不可執行(noexec)的儲存媒介,不能直接用來作為可執行文件的運行目錄,使用前應該把可執行文件復制到APP內部存儲下再運行。所以使用System.load加載so時要註意把so拷貝至/data/data/
so文件註意事項
1.很多設備都支持多於一種的ABI。但最好是針對特定平臺提供相應平臺的二進制包,從而得到更好的性能。
2.你應該盡可能的提供專為每個ABI優化過的.so文件,但要麽全部支持,要麽都不支持:你不應該混合著使用。你應該為每個ABI目錄提供對應的.so文件。
當一個應用安裝在設備上,只有該設備支持的CPU架構對應的.so文件會被安裝。在x86設備上,libs/x86目錄中如果存在.so文件的話,會被安裝,如果不存在,則會選擇armeabi-v7a中的.so文件,如果也不存在,則選擇armeabi目錄中的.so文件(因為x86設備也支持armeabi-v7a和armeabi)。
3.使用NDK時,你可能會傾向於使用最新的編譯平臺,但事實上這是錯誤的,因為NDK平臺不是後向兼容的,而是前向兼容的。推薦使用app的minSdkVersion對應的編譯平臺。
Android so文件