本篇簡單介紹Android
中so文件相關事項。html
目前主流的CPU架構:x86,ARM,MIPSjava
它們採用的指令集又分爲CISC(複雜指令集)和RISC(精簡指令集)兩種android
CISC(複雜指令集)
:服務器
1.指令系統龐大,指令功能複雜,指令格式、尋址方式多網絡
2.絕大多數指令需多個機器週期完成架構
3.各類指令均可訪問存儲器app
4.採用微程序控制ide
5.有專用寄存器,少許函數
6.難以用優化編譯技術生成高效的目標代碼程序性能
RISC(精簡指令集)
:
1.統一指令編碼,可快速解譯;
2.泛用的暫存器,全部暫存器可用於全部內容,以及編譯器設計的單純化
3.單純的尋址模式
4.硬件中支援少數資料型別
x86``CISC
絕大部分pc都是x86架構。
ARM``RISC
普遍應用在嵌入式系統
MIPS``RISC
普遍被使用在許多電子產品、網絡設備、我的娛樂裝置與商業裝置上
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 | 支持 |
so機制讓開發者最大化利用已有的C和C++代碼,達到重用的效果,利用軟件世界積累了幾十年的優秀代碼
so是二進制,沒有解釋編譯的開消,用so實現的功能比純java實現的功能要快
so內存分配不受Dalivik/ART的單個應用限制,減小OOM
相對於java代碼,二進制代碼的反編譯難度更大,一些核心代碼能夠考慮放在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只須要傳入so在Android.mk中定義的LOCAL_MODULE的值便可,系統會調用System.mapLibraryName把這個libName轉化成對應平臺的so的全稱並去嘗試尋找這個so加載。好比咱們的so文件全名爲libmath.so,加載該動態庫只須要傳入math便可:
System.loadLibrary("math");
官方介紹:
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/
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對應的編譯平臺。