分类: Android平台
2020-04-02 14:29:04
最近我们发现很多用户在接入虹软ArcFace人脸识别SDK时,经常会遇到动态库加载失败的相关问题。本文详细介绍从编译动态库(.so)到程序调用so的整个流程,模拟在加载虹软人脸识别so文件时经常遇到的一些问题,帮助大家了解这些问题出现的原因以及解决方法。
点击(此处)折叠或打开
原因: 在安装应用时,APK中指定的ABI目录下没有发现指定的动态库,寻找apk中动态库的规则详见 developer.android.google.cn/ndk/guides/…
导致这个问题的间接原因很多,比如:
解决方案: 确保被安装程序中包含的目标设备支持的ABI的动态库,可以解压APK检查动态库是否存在。
点击(此处)折叠或打开
原因: 在64位库目录下存放的动态库文件是32位的。 例如将armeabi-v7a的动态库存放在arm64-v8a目录下,并安装在支持arm64-v8a的设备上,就会导致这样的错误。
解决方案: 确保动态库ABI正确,一般在拷贝文件时拷贝ABI文件夹即可。
点击(此处)折叠或打开
原因: 动态库存在,但是文件是空的。
解决方案: 重新将动态库引入工程。
点击(此处)折叠或打开
- java.lang.UnsatisfiedLinkError: No implementation found for int b.a.a.b.b(android.content.Context, java.lang.String, java.lang.String) (tried Java_b_a_a_b_b and Java_b_a_a_b_b__Landroid_content_Context_2Ljava_lang_String_2Ljava_lang_String_2)
- at b.a.a.b.b(Native Method)
- at b.a.a.b.a(:182)
原因: 在Java函数确定后,按照固定的规则去寻找native函数找不到。一般情况下都是Java代码混淆导致的。
解决方案: 修改混淆配置文件,确保相关的Java代码不被混淆。
点击(此处)折叠或打开
原因: 在动态库中,以指定的Java签名无法找到对应的Java类、函数、变量。
解决方案: 修改混淆配置文件,确保相关的Java代码不被混淆。
以上是常见的crash与基本原因和解决方案的介绍,接下来,我们来自己编译动态库并使用,了解下这些问题是怎么出现的。
CMakeLists.txt里的内容比较简单,将hello.cpp编译成一个名为libhello-sdk.so的动态库
点击(此处)折叠或打开
在这个文件中,使用JNI静态注册和动态注册的方式定义了两个函数,并在JNI_Onload中对需要动态注册的函数进行注册:
Java_com_arcsoft_functionregisterdemo_MainActivity_hello 需要被静态注册的函数,在Java中定义的native函数首次被调用时,会由JVM按照固定的规则去寻找native函数并注册。这个规则一般是:Java_包名_类名_函数名。具体的实现,大家感兴趣的话,可参考这个地址中的JniShortName()和JniLongName():
dynamicRegisterFunction 需要被动态注册的函数,一般在JNI_OnLoad中进行注册。
JNI_OnLoad 动态库被加载时,会被执行的函数,在这里对dynamicRegisterFunction进行注册。对于JNI_OnLoad函数被调用的具体实现,大家感兴趣的话,可参考 的第1009至1024行。
点击(此处)折叠或打开
配置CMakeLists.txt所在路径,且配置当前编译的abi仅为armeabi-v7a和arm64-v8a
点击(此处)折叠或打开
我们可以选择直接打包apk安装运行,但这里为了模拟调用SDK,我们可以选择手动打包动态库再拿来使用。
至此,libhello-sdk.so编译完成,接下来把工程的Native构建配置删除,像接入SDK一样使用这两个动态库。
由于我们已经编译好动态库了,现在去除gradle中的Native构建配置,否则会报More than one file was found with OS independent path 'XXXX'的错误
点击(此处)折叠或打开
点击(此处)折叠或打开
操作方式:jniLibs目录下不保留任何动态库
日志如下,在加载动态库时,由于在几个库目录寻找所需的动态库没找到,于是报了UnsatisfiedLinkError错误:couldn't find "libhello-sdk.so"
点击(此处)折叠或打开
2.3.2 加载的动态库ABI不对
操作方式:将armeabi-v7a的动态库放到arm64-v8a目录下
日志如下,在加载动态库时,虽然库是存在的,但是ABI不对,于是报了UnsatisfiedLinkError错误:"XXXX" is 32-bit instead of 64-bit
点击(此处)折叠或打开
操作方式:删除动态库文件再撤销删除
这可能是Android Studio的一个小问题,有时删除文件后撤销删除,文件虽然能够重新出现,但是大小为0。
日志如下,在加载动态库时,虽然库是存在的,但是文件大小为0,于是报了UnsatisfiedLinkError错误:dlopen failed: file offset for the library "XXXX" >= file size: 0 >= 0
点击(此处)折叠或打开
点击(此处)折叠或打开
操作方式:混淆Java代码 混淆Java代码也可能会导致加载动态库时直接crash。
对于JNI动态注册,我们一般会在JNI_OnLoad中进行函数注册,此时native函数由函数指针确定,JVM根据指定的Java函数名和函数签名寻找对应的Java函数,若找不到,则会直接报错,错误内容一般如下:
点击(此处)折叠或打开
点击(此处)折叠或打开
点击(此处)折叠或打开
点击(此处)折叠或打开
此时运行效果正常,需要注意的是,如果自己编写native函数,需要在native反射修改java中的field,还需要确保需要被反射的field不被混淆。
若以下其中一项不满足,就无法成功调用动态库:
当遇到错误时,日志中一般都有一些关键信息,能看到错误的具体原因,我们可以分析日志,了解排错方法。