赞
踩
本文整理了JNI开发中常见的问题和解决方案。
智能语音交互SDK工程模块编译时指定的ANDROID_PLATFORM统一是23:-DANDROID_PLATFORM=23
,ndk使用的是版本是17,在手上现有设备跑的都没问题,但是在一个新采购的temi移动机器人上跑不起来,定位到问题是信号处理库报了下面问题:
java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memclr4" referenced by "/data/app/com.xxx.xxxx.robot-2/lib/arm/libkeos_signal_processing.so"...
最开始以为是信号处理库中用到了什么不兼容方法,把库的实现都改为空实现后仍报该错误,网上查询到是target version和目标设备不对应会报该错,机器人的系统版本是6.0,信号处理库编译时Application.mk中设置的APP_PLATFORM := android-26
,修改完后就果然解决。
https://github.com/android/ndk/issues/126
https://github.com/android/ndk/issues/1188
官网中对该问题有介绍:
当尝试加载原生库时,这些错误会显示在日志中。此符号可以是 __aeabi_*
中的任意一个;其中 __aeabi_memcpy
和 __aeabi_memclr
可能是最常见的。此问题已记录在问题 126 中
_FILE_OFFSET_BITS=64
with older API levels与上一个类似,也是API版本问题。
在统一头文件之前,NDK 并不支持 _FILE_OFFSET_BITS=64
。如果在构建应用时定义了该选项,系统会静默地忽略它。现在,_FILE_OFFSET_BITS=64
选项受统一头文件的支持,但在旧版本的 Android 中,很少有 off_t
API 可用作 off64_t
变体。因此,如果将该功能与旧版 API 级别搭配使用,会导致可用函数减少。
问题: build 请求获得的 minSdkVersion
中不存在的 API。
解决方案:停用 _FILE_OFFSET_BITS=64
或提高 minSdkVersion
。
正常情况我们在方法里面申请的local reference会在方法执行完后自动释放,所以一直没有太在意local reference的释放,结果在一个线程的looper方法中有两个jstring使用完一直没释放,导致交互多轮后泄露崩溃。
FindClass
找不到类常见问题类型:
java/lang/String
。如果要查找某个数组类,则需要以适当数量的英文方括号开头,并且还必须用“L”和“;”将该类包裹起来,因此 String
的一维数组将是 [Ljava/lang/String;
。如果要查找内部类,使用“$”而不是“.”,可以在 .class 文件上使用 javap
是查找类的内部名称。FindClass
需要在与代码关联的类加载器的启动类搜索。它会检查调用堆栈,如下所示: Foo.myfunc(Native Method)
Foo.main(Foo.java:10)
最顶层的方法是 Foo.myfunc
。FindClass
会查找与 Foo
类关联的 ClassLoader
对象并使用它。
采用这种方法一般情况可以满足我们的需求。但是如果在native线程(比如通过调用 pthread_create
,然后使用 AttachCurrentThread
进行附加),可能会有问题。因为在这个线程中没有堆栈帧,如果从此线程调用 FindClass
,JavaVM 会在“系统”类加载器(而不是与应用关联的类加载器)中启动,因此尝试查找特定于应用的类将失败。
可以通过以下几种方法来解决此问题:
JNI_OnLoad
中执行一次 FindClass
查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad
过程中发出的任何 FindClass
调用都会使用与调用 System.loadLibrary
的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果我们的应用代码要加载库,FindClass
会使用正确的类加载器。Foo.class
,从而将类的实例传递给需要它的函数。ClassLoader
对象的引用,然后直接发出 loadClass
调用,这个比较麻烦一些,而且缓存classloader可能也会存在一些问题。java/lang/ClassLoader
的getClassLoader
,然后调用findClass
:static jobject gClassLoader; static jmethodID gFindClassMethod; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { gJvm = pjvm; // cache the JavaVM pointer auto env = getEnv(); //replace with one of your classes in the line below auto randomClass = env->FindClass("com/example/RandomClass"); jclass classClass = env->GetObjectClass(randomClass); auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod); gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); return JNI_VERSION_1_6; } jclass findClass(const char* name) { return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStrin
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。