想必很多开发者和我们一样,遇到过许多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的异常。

参考

[持续更新]UnsatisfiedLinkError常见问题及解决方案的更多相关文章

  1. 关于ASP.NET MVC开发设计中出现的问题与解决方案汇总 【持续更新】

    最近一直用ASP.NET MVC 4.0 +LINQ TO SQL来开发设计公司内部多个业务系统网站,在这其中发现了一些问题,也花了不少时间来查找相关资料或请教高人,最终都还算解决了,现在我将这些问题 ...

  2. HBase常见问题答疑解惑【持续更新中】

    HBase常见问题答疑解惑[持续更新中] 本文对HBase开发及使用过程中遇到过的常见问题进行梳理总结,希望能解答新加入的HBaser们的一些疑惑. 1. HTable线程安全吗? HTable不是线 ...

  3. webpack1.x环境配置与打包基础【附带各种 "坑" 与解决方案!持续更新中...】

    首先介绍传统模块化开发的主流方案: 1.基与CMD的sea.js,玉伯提出的解决方案,据说原来京东团队在使用.用时才定义,就近加载. 2.基于AMD的require.js,之前在用.提前声明与定义.国 ...

  4. BAT 前端开发面经 —— 吐血总结 前端相关片段整理——持续更新 前端基础精简总结 Web Storage You don't know js

    BAT 前端开发面经 —— 吐血总结   目录 1. Tencent 2. 阿里 3. 百度 更好阅读,请移步这里 聊之前 最近暑期实习招聘已经开始,个人目前参加了阿里的内推及腾讯和百度的实习生招聘, ...

  5. 消息队列面试题、RabbitMQ面试题、Kafka面试题、RocketMQ面试题 (史上最全、持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  6. java视频教程 Java自学视频整理(持续更新中...)

    视频教程,马士兵java视频教程,java视频 1.Java基础视频 <张孝祥JAVA视频教程>完整版[RMVB](东西网) 历经5年锤炼(史上最适合初学者入门的Java基础视频)(传智播 ...

  7. ( 译、持续更新 ) JavaScript 上分小技巧(四)

    后续如有内容,本篇将会照常更新并排满15个知识点,以下是其他几篇译文的地址: 第一篇地址:( 译.持续更新 ) JavaScript 上分小技巧(一) 第二篇地址:( 译.持续更新 ) JavaScr ...

  8. ( 译、持续更新 ) JavaScript 上分小技巧(三)

    最近家里杂事较多,自学时间实在少的可怜,所以都在空闲时间看看老外写的内容,学习之外顺便翻译分享~等学习的时间充足些再写写自己的一些学习内容和知识点分析(最近有在接触的:复习(C#,SQL).(学习)T ...

  9. 系列文章:老项目的#iPhone6与iPhone6Plus适配#(持续更新中,更新日期2014年10月12日 星期日 )

    本文永久地址为http://www.cnblogs.com/ChenYilong/p/4020399.html ,转载请注明出处. ********************************** ...

随机推荐

  1. JAVA数组所占内存大小的对比

    1.两个数据模型 第一个是基本类型数组,第二个使用的是Float对象数组 public class SummaryModel{ private float[] summaryData; public ...

  2. 在线视频转gif动画工具 在线视频转gif动画工具下载

    在线视频转gif动画工具 在线视频转gif动画工具下载 http://www.leawo.cn/space-1723875-do-thread-id-60715.html http://www.lea ...

  3. DataGridView很详细的用法(转载)

    一.DataGridView 取得或者修改当前单元格的内容: 当前单元格指的是 DataGridView 焦点所在的单元格,它可以通过 DataGridView 对象的 CurrentCell 属性取 ...

  4. axis2带list的报文,对象和xml的转换

    import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.dom4j. ...

  5. 软件包管理 之 Fedora/Redhat 在线安装更新软件包,yum 篇 ── 给新手指南

    在本文中,我们主要解介绍 Fedora core 4.0 通过软件包管理工具yum来在线安装更新软件:关于apt工具应用,我们会在另外一篇中介绍: 一. yum 的使用:有些初学Linux的弟兄可能问 ...

  6. Quartz 2D绘制简单图形

    在Quartz 2D中,绘图是通过图形上下文进行绘制的,以下绘制几个简单的图形 首先先创建一个QuartzView.swift文件继承自UIView,然后实现drawRect方法: import UI ...

  7. 1.C#中通过委托Action消除重复代码

    阅读目录 一:重复的代码  二:使用委托消除重复代码     一:重复的代码    我们在写一些方法的时候,会在里面可能出现异常的地方使用try catch语句,这样每个方法都会有try catch语 ...

  8. XSHELL配色方案及导入配色方案的方法

    [ubuntu] text(bold)=ffffff magenta(bold)=ad7fa8 text=ffffff white(bold)=eeeeec green=4e9a06 red(bold ...

  9. (笔记)Linux内核学习(四)之系统调用

    一 用户空间和内核空间 Linux内核将这4G字节虚拟地址空间的空间分为两部分: l  将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”. l  ...

  10. SVN: bdb: BDB1538 Program version 5.3 doesn't match environment version 4.7

    Q:bdb: BDB1538 Program version 5.3 doesn't match environment version 4.7 A: svnadmin recover /var/wh ...