想必不少開發者和咱們同樣,遇到過許多UnsatisfiedLinkError的困難,着實使人頭疼,如今總結一下,但願能幫助更多的人。java
lib庫不一樣目錄下的SO文件良莠不齊。android
發現不少APK包打出來,lib目錄下同時帶着armeabi、armeabi-v7a,可是armeabi目錄下可能有3個SO,而armeabi-v7a下只有2個SO,更有甚者還有armeabi、armeabi-v7a、x8六、x86_6四、arm64-v8a所有都有,可是不一樣目錄下的SO個數都不同。c++
這樣打出來的APK包,在安裝的時候會讓Android系統「很爲難」,它搞不清到底該選擇哪一個SO來安裝。有時可能會形成某個SO的漏安裝,那麼在APP運行的時候加載SO時就會出現異常了。安全
解決方案:架構
一、只保留lib下的一個目錄足夠(armeabi或armeabi-v7a保留一個),其餘目錄所有不用配置。app
二、若是想繼續多配置幾個CPU架構的lib目錄,那就所有配置齊全。實際上有時候很難作到,特別是當須要使用三方庫的SO的時候,每每並不那麼容易找的齊全。因爲所有打齊全會對APK的體積有增長,因此仍是推薦第一種方案。函數
lib庫目錄下的SO不符合相應的CPU架構。ui
同上面的問題差很少,有些APK包打出來,同時配置了armeabi和arm64-v8,可是卻在arm64-v8放置了某個或多個armeabi版本的SO,那麼在APP運行的時候就會報相似的錯誤:"lib_xyz.so" is 32-bit instead of 64-bitcode
64-bit下使用System.load加載SO:"lib_xyz.so" is 32-bit instead of 64-bit開發
Found an explanation: 64-bit Android can use 32-bit native libraries as a fallback, only if System.loadlLibrary() can't find anything better in the default search path. You get an UnsatisfiedLinkError if you force the system to load the 32-bit library using System.load() with the full library path. So the first workaround is using System.loadLibrary() instead of System.load().
64-bit處理器能夠向下兼容32-bit指令集,便可以運行32-bit動態庫,因此APK包仍然能夠只保留lib下的一個目錄足夠(armeabi或armeabi-v7a保留一個),其餘目錄所有不用配置。
有一種組合錯誤,就是APK的lib庫打的良莠不齊,又在64-bit下使用System.load加載SO。
有一個APP在MX5(android5.0.1)下出現瞭如下異常:
Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.xxx.pris/app_lib/libPDEEngine.so" is 32-bit instead of 64-bit
首先能夠大體知道這是一個64位的機器,查看雲捕展現的機器信息,確實是arm64-v8a。首先就是看APK的lib目錄打的對不對,果真
armeabi下有12個SO,而armeabi-v7a下卻只有11個SO。可是他們使用了雲捕代碼來嘗試安全加載SO來下降UnsatisfiedLinkError的異常。
public static void loadLibrary(final Context context, final String library) { if (context == null) { throw new IllegalArgumentException("Given context is null"); } if (TextUtils.isEmpty(library)) { throw new IllegalArgumentException("Given library is either null or empty"); } try { System.loadLibrary(library); return; } catch (final UnsatisfiedLinkError ignored) { // :-( CrashHandler.leaveBreadcrumb("ReLinker: System.loadLibrary failed"); } final File workaroundFile = getWorkaroundLibFile(context, library); if (!workaroundFile.exists()) { unpackLibrary(context, library); } System.load(workaroundFile.getAbsolutePath()); }
能夠看出,若是由於SO打的良莠不齊致使了APK在安裝的時候SO就已經有遺漏的沒有被安裝進lib的加載目錄。那麼System.loadLibrary的時候便會有異常,而後代碼嘗試解壓並釋放所須要的SO文件,可是這個時候只能經過System.load來加載了,又因爲當前是arm64-v8a的機器,因此就出現了Use 32-bit jni libraries on 64-bit android - Stack Overflow的問題。
解決辦法:
因爲Native層須要註冊到java層函數,若是java層對應的類名和函數名在打包的時候被混淆了,確定是會出現異常的。此類問題比較定位解決,可是也比較容易忘記。解決辦法就是在proguard混淆時keep掉對應的類和函數。
註冊方式不對,或已經被其餘類註冊。
早期的崩潰捕獲功能是在加殼裏用的,後來把崩潰捕獲的代碼單獨抽出爲雲捕SDK,爲了保證複用,加殼和雲捕SDK共同使用一個libbugrpt.so。外殼若是註冊了,則雲捕再也不註冊。若是外殼已經註冊過了,雲捕仍然要繼續註冊使用,就會出現上面的錯誤。解決辦法是:當外殼已經註冊啓用了崩潰捕獲,則雲捕再也不啓動。
empty/missing DT_HASH in "libxxxx.so" (built with --hash-style=gnu?)
java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH in "libxxxx.so" (built with --hash-style=gnu?) at java.lang.Runtime.loadLibrary(Runtime.java:371) at java.lang.System.loadLibrary(System.java:989)
c++ - Android NDK UnsatisfiedLinkError: "dlopen failed: empty/missing DT_HASH" - Stack Overflow
若是問題出現時能夠嘗試經過以上的幾種方法來排查,若是有其餘沒有羅列的情形能夠發給我,我將會持續收集整理並更新,以期幫助更多的開發者解決問題。
若是想要規避以上的問題,最好的辦法就是打好打全相應CPU架構的SO文件。
另外你也能夠直接使用雲捕SDKRelinker.loadLibrary功能,來幫助你安全加載SO以下降此類UnsatisfiedLinkError的異常。