1. 程式人生 > >Android so文件

Android so文件

loader 寄存器 oom androi armv8 進制 打包 x86 2014年

本篇簡單介紹Android中so文件相關事項。

CPU架構

目前主流的CPU架構:x86,ARM,MIPS

它們采用的指令集又分為CISC(復雜指令集)和RISC(精簡指令集)兩種

CISC(復雜指令集)

1.指令系統龐大,指令功能復雜,指令格式、尋址方式多

2.絕大多數指令需多個機器周期完成

3.各種指令都可訪問存儲器

4.采用微程序控制

5.有專用寄存器,少量

6.難以用優化編譯技術生成高效的目標代碼程序

RISC(精簡指令集):

1.統一指令編碼,可快速解譯;

2.泛用的暫存器,所有暫存器可用於所有內容,以及編譯器設計的單純化

3.單純的尋址模式

4.硬件中支援少數資料型別

  • x86``CISC

    絕大部分pc都是x86架構。

  • ARM``RISC廣泛應用在嵌入式系統

  • MIPS``RISC廣泛被使用在許多電子產品、網絡設備、個人娛樂裝置與商業裝置上

CPU架構和ABI

Android系統目前支持以下七種不同的CPU架構:ARMv5ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8MIPS64x86_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文件