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


想必很多開發者和我們一樣,遇到過許多UnsatisfiedLinkError的困難,着實令人頭疼,現在總結一下,希望能幫助更多的人。

常見錯誤

  • 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文件參差不齊。

    發現很多APK包打出來,lib目錄下同時帶着armeabi、armeabi-v7a,但是armeabi目錄下可能有3個SO,而armeabi-v7a下只有2個SO,更有甚者還有armeabi、armeabi-v7a、x86、x86_64、arm64-v8a全部都有,但是不同目錄下的SO個數都不一樣。

    這樣打出來的APK包,在安裝的時候會讓Android系統“很為難”,它搞不清到底該選擇哪個SO來安裝。有時可能會造成某個SO的漏安裝,那么在APP運行的時候加載SO時就會出現異常了。

    解決方案:

    1、只保留lib下的一個目錄足夠(armeabi或armeabi-v7a保留一個),其他目錄全部不用配置。

    2、如果想繼續多配置幾個CPU架構的lib目錄,那就全部配置齊全。實際上有時候很難做到,特別是當需要使用三方庫的SO的時候,往往並不那么容易找的齊全。由於全部打齊全會對APK的體積有增加,所以還是推薦第一種方案。

  • lib庫目錄下的SO不符合相應的CPU架構。

    同上面的問題差不多,有些APK包打出來,同時配置了armeabi和arm64-v8,但是卻在arm64-v8放置了某個或多個armeabi版本的SO,那么在APP運行的時候就會報類似的錯誤:"lib_xyz.so" is 32-bit instead of 64-bit

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

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM