最近我们发现很多用户在接入虹软ArcFace人脸识别SDK时,经常会遇到动态库加载失败的相关问题。本文详细介绍从编译动态库(.so)到程序调用so的整个流程,模拟在加载虹软人脸识别so文件时经常遇到的一些问题,帮助大家了解这些问题出现的原因以及解决方法。
一、 ArcFace库加载常见错误
1.1 找不到动态库
java.lang.UnsatisfiedLinkError: couldn't find "libarcsoft_face_engine.so"
原因:
在安装应用时,APK中指定的ABI目录下没有发现指定的动态库,寻找apk中动态库的规则详见
https://developer.android.google.cn/ndk/guides/abis?hl=en#aen
导致这个问题的间接原因很多,比如:
-
Android工程中没有指定的动态库
-
Android工程中动态库存放位置错误
-
设备支持的最高ABI是armeabi-v7a,而apk只有arm64-v8a的动态库
解决方案:
确保被安装程序中包含的目标设备支持的ABI的动态库,可以解压APK检查动态库是否存在。
1.2 加载的动态库ABI不对
java.lang.UnsatisfiedLinkError: "libarcsoft_face_engine.so" is 32-bit instead of 64-bit
原因:
在64位库目录下存放的动态库文件是32位的。
例如将armeabi-v7a的动态库存放在arm64-v8a目录下,并安装在支持arm64-v8a的设备上,就会导致这样的错误。
解决方案:
确保动态库ABI正确,一般在拷贝文件时拷贝ABI文件夹即可。
1.3 动态库文件长度为0
java.lang.UnsatisfiedLinkError: dlopen failed: file offset for the library ".../libarcsoft_face_engine.so" >= file size: 0 >= 0
原因:
动态库存在,但是文件是空的。
解决方案:
重新将动态库引入工程。
1.4 执行函数时找不到XXXX函数
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代码不被混淆。
1.5 在加载动态库时出现crash
JNI DETECTED ERROR IN APPLICATION: JNI RegisterNatives called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.arcsoft.face.FaceEngine"
原因:
在动态库中,以指定的Java签名无法找到对应的Java类、函数、变量。
解决方案:
修改混淆配置文件,确保相关的Java代码不被混淆。
以上是常见的crash与基本原因和解决方案的介绍,接下来,我们来自己编译动态库并使用,了解下这些问题是怎么出现的。
二、自己编译并使用动态库
2.1. 编译动态库
2.1.1 CMakeLists.txt
CMakeLists.txt
里的内容比较简单,将hello.cpp
编译成一个名为libhello-sdk.so
的动态库
add_library(
hello-sdk
SHARED
hello.cpp
)
2.1.2 hello.cpp
在这个文件中,使用JNI静态注册和动态注册的方式定义了两个函数,并在JNI_Onload
中对需要动态注册的函数进行注册:
-
Java_com_arcsoft_functionregisterdemo_MainActivity_hello
需要被静态注册的函数,在Java中定义的native函数首次被调用时,会由JVM按照固定的规则去寻找native函数并注册。这个规则一般是:
Java_包名_类名_函数名
。具体的实现,大家感兴趣的话,可参考这个地址中的JniShortName()
和JniLongName()
: http://androidxref.com/9.0.0_r3/xref/art/runtime/art_method.cc -
dynamicRegisterFunction
需要被动态注册的函数,一般在
JNI_OnLoad
中进行注册。 -
JNI_OnLoad
动态库被加载时,会被执行的函数,在这里对
dynamicRegisterFunction
进行注册。对于JNI_OnLoad
函数被调用的具体实现,大家感兴趣的话,可参考
http://androidxref.com/9.0.0_r3/xref/art/runtime/java_vm_ext.cc 的第1009至1024行。
#include <jni.h>
#include <string>
// 静态注册的函数,对应MainActivity类中的hello函数