想必不少开发者和咱们同样,遇到过许多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的异常。