[持續更新]UnsatisfiedLinkError常見問題及解決方案

想必不少開發者和咱們同樣,遇到過許多UnsatisfiedLinkError的困難,着實使人頭疼,如今總結一下,但願能幫助更多的人。java

常見錯誤

  • lib庫不一樣目錄下的SO文件良莠不齊。
  • lib庫目錄下的SO不符合相應的CPU架構。
  • 64-bit下使用System.load加載SO:"lib_xyz.so" is 32-bit instead of 64-bit
  • java代碼混淆致使。
  • 註冊方式不對,或已經被其餘類註冊。
  • empty/missing DT_HASH in "libxxxx.so" (built with --hash-style=gnu?)

出錯現象及解決方案

  • 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開發

    Use 32-bit jni libraries on 64-bit android - Stack Overflow

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的問題。

解決辦法:

  1. APK包打的時候把SO打的齊全了,並建議只保留一個目錄足夠(armeabi或armeabi-v7a保留一個)。
  2. 雲捕SDK在發現上述問題以後,嘗試解壓釋放SO的時候,把解壓目錄設置到lib的加載路徑順序裏去,並繼續使用System.loadLibrary來加載(而不是System.load)。並在第一次System.loadLibrary出現異常時,麪包屑告訴足夠多的信息,例如是不是SO不存在。
  • java代碼混淆致使。

因爲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的異常。

參考

相關文章
相關標籤/搜索