[持续更新]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
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的问题。
解决办法:
- APK包打的时候把SO打的齐全了,并建议只保留一个目录足够(armeabi或armeabi-v7a保留一个)。
- 云捕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常见问题及解决方案的更多相关文章
- 关于ASP.NET MVC开发设计中出现的问题与解决方案汇总 【持续更新】
最近一直用ASP.NET MVC 4.0 +LINQ TO SQL来开发设计公司内部多个业务系统网站,在这其中发现了一些问题,也花了不少时间来查找相关资料或请教高人,最终都还算解决了,现在我将这些问题 ...
- HBase常见问题答疑解惑【持续更新中】
HBase常见问题答疑解惑[持续更新中] 本文对HBase开发及使用过程中遇到过的常见问题进行梳理总结,希望能解答新加入的HBaser们的一些疑惑. 1. HTable线程安全吗? HTable不是线 ...
- webpack1.x环境配置与打包基础【附带各种 "坑" 与解决方案!持续更新中...】
首先介绍传统模块化开发的主流方案: 1.基与CMD的sea.js,玉伯提出的解决方案,据说原来京东团队在使用.用时才定义,就近加载. 2.基于AMD的require.js,之前在用.提前声明与定义.国 ...
- BAT 前端开发面经 —— 吐血总结 前端相关片段整理——持续更新 前端基础精简总结 Web Storage You don't know js
BAT 前端开发面经 —— 吐血总结 目录 1. Tencent 2. 阿里 3. 百度 更好阅读,请移步这里 聊之前 最近暑期实习招聘已经开始,个人目前参加了阿里的内推及腾讯和百度的实习生招聘, ...
- 消息队列面试题、RabbitMQ面试题、Kafka面试题、RocketMQ面试题 (史上最全、持续更新、吐血推荐)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- java视频教程 Java自学视频整理(持续更新中...)
视频教程,马士兵java视频教程,java视频 1.Java基础视频 <张孝祥JAVA视频教程>完整版[RMVB](东西网) 历经5年锤炼(史上最适合初学者入门的Java基础视频)(传智播 ...
- ( 译、持续更新 ) JavaScript 上分小技巧(四)
后续如有内容,本篇将会照常更新并排满15个知识点,以下是其他几篇译文的地址: 第一篇地址:( 译.持续更新 ) JavaScript 上分小技巧(一) 第二篇地址:( 译.持续更新 ) JavaScr ...
- ( 译、持续更新 ) JavaScript 上分小技巧(三)
最近家里杂事较多,自学时间实在少的可怜,所以都在空闲时间看看老外写的内容,学习之外顺便翻译分享~等学习的时间充足些再写写自己的一些学习内容和知识点分析(最近有在接触的:复习(C#,SQL).(学习)T ...
- 系列文章:老项目的#iPhone6与iPhone6Plus适配#(持续更新中,更新日期2014年10月12日 星期日 )
本文永久地址为http://www.cnblogs.com/ChenYilong/p/4020399.html ,转载请注明出处. ********************************** ...
随机推荐
- atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证
atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证 1. 需求,表单验证需要弹框式,但目前easyui ms绑定死了tooltip式样 1 2. 表 ...
- Python之线程池
版本一: #!/usr/bin/env python # -*- coding:utf-8 -*- import Queue import threading class ThreadPool(obj ...
- bzoj 1191: [HNOI2006]超级英雄Hero
1191: [HNOI2006]超级英雄Hero Time Limit: 10 Sec Memory Limit: 162 MB 二分图匹配... Description 现在电视台有一种节目叫做超 ...
- seajs加载jquery时提示$ is not a function该怎么解决
这篇文章主要介绍了seajs加载jquery时提示$ is not a function该怎么解决的相关资料,需要的朋友可以参考下 jquery1.7以上的都支持模块化加载,只是jquery默认的是支 ...
- iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示
iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端. 打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面. ...
- CDH的几个包的下载地址
https://archive.cloudera.com/cdh5/parcels/5.3.0/ http://archive.cloudera.com/cm5/installer/5.3.0/ ht ...
- zookeeper节点Watch机制实例展示
znode以某种方式发生变化时,“观察”(watch)机制可以让客户端得到通知.可以针对ZooKeeper服务的“操作”来设置观察,该服务的其他 操作可以触发观察. 实现Watcher,复写proce ...
- Android判断当前线程是否是主线程的方法
开发过程中有时候会在Thread类中执行某些操作,有些操作会由于Android版本的不同,尤其是低版本而Crash,因此必要的时候会查看某些容易引起crash的操作是否是在主线程,这里举三种方法: 方 ...
- 关于 c# 操作 world
把数据存放在datatable 中并循环取出来数据然后再保存在world中 protected void ExportToWord(DataSet Ads) { try { Object Nothin ...
- linux 系统性能指标采样脚本
以下脚本写于redmine性能排查时,用于定位系统性能瓶颈的采样,源地址为~/performanceLog/collectLog.sh中,计划放入github的代码片段库中. 注: 如果mysql的地 ...