1. 程式人生 > >android開發 載入so庫的解析和出現的各種錯誤分析

android開發 載入so庫的解析和出現的各種錯誤分析

一.android目前有幾種cpu架構?

早期的Android系統幾乎只支援ARMv5的CPU架構,你知道現在它支援多少種嗎?7種!

Android系統目前支援以下七種不同的CPU架構:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯著一個相應的ABI。

二.每種cpu架構對應的資料夾名稱是什麼?

在Android系統上,每一個CPU架構對應一個ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64

也就是說我們要想支援全部系統的手機需要放全所有不同版本的so庫。

ARM架構屬於RISC指令集,指令集精簡、指令等長,雖然這樣的設計可以提高處理效率,但在遇到複雜的指令後,就需要更多的簡單指令來堆砌複雜任務;ARM從來只是設計低功耗處理器。其宗旨是設計低功耗處理器,這是他們的強項。

armeabi:ARM架構的預設選項,支援基於 ARM* v5TE 的裝置,支援軟浮點運算,但不支援硬體輔助浮點運算,支援所有的 ARM* 裝置。

armeabi-v7a:armeabi-v7a 向下相容,在相容 armeabi 的基礎上,支援基於 ARM* v7 的裝置,支援硬體 FPU 指令,支援硬體浮點運算,目前大部分機器都屬於armeabi-v7a。

arm64-v8a:arm64-v8a向下相容 armeabi 和 armeabi-v7a,最主要的區別在於 arm64-v8a 支援64位,在 MIPS64 架構上增加了 ARMv7 架構中已經擁有的的TrustZone技術、虛擬化技術及NEON advanced SIMD技術等特性(ARM收購MIPS)。架構中包含兩個執行狀態:AArch32(也就是我們常說的ARMv7)和AArch64(ARMv8),也就是說64位的ARM處理器中同時包含著32位的ARMv7和64位的ARMv8兩種架構,直接導致每種架構所擁有的電晶體減半。

X86構架是英特爾推出的一種複雜指令集,用於控制晶片的執行的程式,目前該構架的處理器已經廣泛運用在PC領域,由於X86構架的處理器晶片在效能上比較強勁,善於執行復雜工作,所以當英特爾進軍移動市場領域後(例如聯想K800),就出現了X86的架構。X86構架屬於典型的CISC,指令集豐富,指令不等長,善於執行復雜工作,更強調序列效能,它的整體運算能力要比只為移動而生的ARM架構強大,並且在PC領域已經廣泛應用,擁有深厚的技術背景。英特爾設計超高效能的桌上型電腦和伺服器處理器,並且的確做的不錯。

x86:英特爾推出的32位CPU架構,生成的二進位制程式碼可支援包含基於硬體的浮點運算的 IA-32 指令集,同時,x86機器基本上可以使用 intel 的 libhounini 專案直接在x86機器上執行僅含armeabi的動態庫程式碼,也就會說x86機器對armeabi也能夠相容,不過效能上會有些損耗。

x86_64:英特爾推出的64位CPU架構,向下相容x86。

mips和mips_64:MIPS是一種高效能的嵌入式CPU構架,其出發點是高效能,主要用於路由器、貓等

三.我們需要每種版本的so庫都放全嗎?

1.看你的so庫的來源,是第三方提供的,還是自己開發的。倘若你是集成了某些第三方的sdk,然後他們提供了多少 ,你最好將它提供的都拷貝到專案中。(但是基本很少有能提供全面的so庫支援的)

2.倘若是自己開發的 ,那就根據自己的情況,開發出對應版本的so庫支援。

四.每種機型就只能載入一種適合自己版本的so庫嗎?

很多裝置都支援多於一種的ABI。例如ARM64和x86裝置也可以同時執行armeabi-v7a和armeabi的二進位制包。但最好是針對特定平臺提供相應平臺的二進位制包,這種情況下執行時就少了一個模擬層(例如x86裝置上模擬arm的虛擬層),從而得到更好的效能(歸功於最近的架構更新,例如硬體fpu,更多的暫存器,更好的向量化等)。

五.如何檢視當前裝置支援那些版本的so庫?

我們可以通過Build.SUPPORTED_ABIS得到根據偏好排序的裝置支援的ABI列表。但你不應該從你的應用程式中讀取它,因為Android包管理器安裝APK時,如果在對應的lib/ABI目錄中存在.so檔案的話,會自動選擇APK包中為對應系統ABI預編譯好的.so檔案。

六.不同的開發環境so庫應該放在哪裡?

  • Android Studio工程放在jniLibs/ABI目錄中(當然也可以通過在build.gradle檔案中的設定jniLibs.srcDir屬性自己指定)

  • Eclipse工程放在libs/ABI目錄中(這也是ndk-build命令預設生成.so檔案的目錄)

  • AAR壓縮包中位於jni/ABI目錄中(.so檔案會自動包含到引用AAR壓縮包的APK中)

  • 最終APK檔案中的lib/ABI目錄中

  • 通過PackageManager安裝後,在小於Android 5.0的系統中,.so檔案位於app的nativeLibraryPath目錄中;在大於等於Android 5.0的系統中,.so檔案位於app的nativeLibraryRootDir/CPU_ARCH目錄中。

七.為什麼一般的專案中只看到armeabi資料夾?

因為所有的x86/x86_64/armeabi-v7a/arm64-v8a裝置都支援armeabi架構的.so檔案。

八.那我們是不是隻放一種armeabi就夠了?

事實上並不是:這不只影響到函式庫的效能和相容性。x86裝置能夠很好的執行ARM型別函式庫,但並不保證100%不發生crash,特別是對舊裝置。64位裝置(arm64-v8a, x86_64, mips64)能夠執行32位的函式庫,但是以32位模式執行,在64位平臺上執行32位版本的ART和Android元件,將丟失專為64位優化過的效能(ART,webview,media等等)。

九.如何檢視我的android手機屬於什麼架構的?

進入命令列:

第一步:輸入 adb shell 回車

第二步:cat /proc/cpuinfo 回車

下面打印出的資訊裡面有 Processor 如下圖:
 

十.到底如何適配,每種適配方案區別?

CPU之間的架構相容

ARMv5 裝置:只支援armeabi

ARMv7 裝置:支援 armeabi 和 armeabi-v7a

ARMv8 裝置:支援 armeabi-v7a、armeabi 和 arm64-v8a

X86 裝置:支援 armeabi(效能有所損耗) 和 x86

x86_64 裝置:支援 x86 和 x86_64

mips 裝置: 支援 mips

mips_64 裝置:支援 mips 和 mips_64

Android中裝置載入so策略

不同CPU架構的android手機載入時會在libs下找自己對應的目錄,從對應的目錄下尋找需要的.so檔案;如果沒有對應的目錄,就會去armeabi下去尋找,如果已經有對應的目錄,卻如果沒有找到對應的.so檔案,也不會去armeabi下去尋找了。

以x86裝置為例,x86裝置會在專案中的 libs資料夾尋找是否含有x86資料夾,如果含有x86資料夾,則預設為該專案有x86對應的so可執行檔案,只有x86資料夾而資料夾下沒有so,程式執行也是會出現find library returned null的錯誤的;如果工程本身不含有x86資料夾,則會尋找armeabi或者armeabi-v7a資料夾,相容執行。以armeabi-v7a裝置為例,該Android裝置當然優先尋找libs目錄下的armeabi-v7a資料夾,同樣,如果只有armeabi-v7a資料夾而沒有 so也是會報錯的;如果找不到armeabi-v7a資料夾,則尋找armeabi資料夾,相容執行該資料夾下的so,但是不能相容執行x86的so。所以專案中如果只含有x86的so,在armeabi和armeabi-v7a也是無法執行的。以上就是不同CPU架構執行時載入so的策略。

目前主流的Android裝置主要是 armeabi-v7a 架構的,然後是 x86 和 armeabi 了。如果同時包含了 armeabi, armeabi-v7a和x86,所有裝置都可以執行,程式在執行的時候去載入不同平臺對應的so,這是較為完美的一種解決方案,但是有時候為了減少apk的大小,不會同時設定 armeabi, armeabi-v7a 和 x86。根據不同的情況,可以進行不同的適配,

1.只適配 armeabi-v7a,因為目前主流機型是 ARMv7,並且 ARMv8 裝置也向下相容了armeabi-v7a,
Facebook、WhatsApp、王者榮耀等就是隻適配了armeabi-v7a。(Google play store下載 Native libs Monitor 進行檢視)。

2.只適配 armeabi,因為 ARMv7 、ARMv8 還是 x86 都相容 armeabi,但是效能都會有些損耗,例如ARMv7 支援硬體浮點運算等沒法體現,x86 支援 armeabi 同樣具有相應的損耗。

3.同時適配 armeabi-v7a 和 armeabi,既能夠支援所有 ARM 架構,同時又能具有 ARMv7 支援硬體浮點運算等特性,例如Line等應用

4.同時適配 x86 和 armeabi,既能支援所有 ARM 架構,又能支援x86架構,唯一的缺點就是沒有了ARMv7 支援硬體浮點運算等一系列特性,例如QQ

5.同時適配 armeabi, armeabi-v7a 和 x86,在效能方面來說是較為完美的方案,只是APK的大小也會隨之的變大

6.其他的一些方案,例如微信只適配了armeabi,但是對於某些需要利用 ARMv7 支援硬體浮點運算等一系列特性的操作,在armeabi目錄下存在v7對應的so檔案,通過程式碼判斷載入不同的so檔案。即達到了減少APK大小的目的,又能達到適配ARMv7等架構以便使用其架構的一些新特性的目的。

十一.so庫開發過程中的一些錯誤彙總!(後續補充)

基本上大部分關於so庫的錯誤 都是   java.lang.UnsatisfiedLinkError

官方解釋     Throw if the java Virtural Machine cannot find an appropriate native-language definition of method declared native意思就是JVM找不到native method的native實現!
所以看到這個錯誤,一般常見的幾種情況分析。

1、低階錯誤——根本木有SO,你載入個球啊!

code     System.loadLibrary(Bugly);
libs     空
執行裝置     Android ARM裝置
執行結果     Crash!java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.tencent.bugly.demo-1/base.apk”],nativeLibraryDirectories=[/vendor/lib, /systemb]]] couldn’t find “libBugly.so”
原因分析     apk安裝時,系統會把apk中libs目錄下armeabi的SO拷貝到應用的私有目錄下。所以libs裡沒有放入SO,執行時肯定找不到SO。
修復方式     新增SO:libs\armeabi\libBugly.so或載入程式碼註釋掉://System.loadLibrary(Bugly) ;

2、進階錯誤——根本木有X86的SO,在X86的裝置上你載入個球啊!

code     System.loadLibrary(Bugly);
libs     libs\armeabi\libBugly.so
執行裝置     Android X86裝置
執行結果     Crash!java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.tencent.bugly.demo-1/base.apk”],nativeLibraryDirectories=[/vendor/lib, /systemb]]] couldn’t find “libBugly.so”
原因分析     apk安裝時,x86裝置上系統會把apk中libs目錄下x86的SO,拷貝到應用的私有目錄下。雖然libs下有armeabi的SO,但沒有放入x86的SO,執行時還是找不到libbugly.so。
修復方式     新增SO:libs\x86\libBugly.so或載入程式碼註釋掉://System.loadLibrary(Bugly) ;

3、大坑——尼瑪,好難發現!

libs     libs\armeabi\libBugly.solibs\armeabi\libBugly2.solibs\armeabi-v7a\libBugly.so
執行裝置     Android ARMv7裝置
執行結果     Crash!java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.tencent.bugly.demo-1/base.apk”],nativeLibraryDirectories=[/vendor/lib, /systemb]]] couldn’t find “libBugly2.so”
原因分析     apk安裝時,系統會把apk中libs目錄下armeabi-v7a整個目錄下的SO拷貝到應用的私有目錄下。因為armeabi-v7a下沒有放入libBugly2.so,執行時找不到libBugly2.so。不同的工具相容的CPU架構不一致,就容易出這個錯誤了!例如:

libBugly.so提供armeabi、armeabi-v7a、x86三種。
但其它產品可能只提供了armeabi。
如果把這些so都直接拷貝進apk,就會因為上述的原因直接crash,會誤以為該Crash是因為不同產品的so不能相容導致的!
修復方式     新增SO:libs\armeabi-v7a\libBugly2.so或直接刪除armeabi-v7a目錄,arm裝置上系統會自動選擇armeabi

4、天坑——尼瑪,巨難發現!

java.lang.UnsatisfiedLinkError中couldn’t find “XX.so”的佔比非常高,上面提的三個場景都是這種錯誤!
但你見過下面這種錯誤嗎?
java.lang.UnsatisfiedLinkError:dlopen failed: “**/*/arm/*.so” has unexpected e_machine: 3
code     if(getArch().contain(“arm”)){//只在arm下載入System.loadLibrary(Bugly) ;}
libs     libs\armeabi\libBugly.so 坑爹實習生放入了x86編譯的libBugly.so(同名很容易出錯)
執行裝置     Android ARM裝置
執行結果     Crash!java.lang.UnsatisfiedLinkError: dlopen failed: “/data/app/com.tencent.bugly.crashreport.demo-2/lib/arm/libBugly.so” has unexpected e_machine: 3
原因分析     apk安裝時,系統把armeabi下的libBugly.so放入應用的私有目錄中了!但這個libBugly.so不是arm的,而是x86編譯的libBugly.so執行時,系統檢察ELF檔案中的e_machine欄位的值,跟arm的不匹配,就會丟擲這個異常了!

5 java.lang.UnsatisfiedLinkError:No implementation found for XXX

這種錯誤也是醉了,說是要建立跟c/cpp寫的程式碼一樣的包名和java檔案

以上錯誤彙總來自於 http://blog.csdn.net/u013278099/article/details/50414438這篇文章

這個so庫的錯誤是我這幾天在做騰訊雲視訊直播的時候出現的一些問題,他們的sdk(1.8.2版本)裡面的問題是:sdk裡面提供的so庫不全,導致出現各種問題。基本上面都介紹到了。

先寫到這裡,希望能讓大家都so庫有一些基本的瞭解。肯定還有很多我沒有介紹到的,希望大家多多交流。

大家可以加入我的qq群:Android開發經驗交流群454430053