最近一个项目要编译深度学习的库,需要用到 opencv 和 JNI,本文档用于记录环境配置中遇到的常见错误以及解决方案
Invalid Gradle JDK configuration found
failed
Invalid Gradle JDK configuration foundInvalid Gradle JDK configuration found. Open Gradle Settings
Change JDK location
解决办法: 删除文件
.idea/gradle.xml
和 .idea/workspace.xml
, 重新编译;
解决办法:Invalid Gradle JDK configuration found
clang++: error: unknown argument: ‘-static-openmp’
原因是NDK版本过高,跟当前的AndroidStudio版本不匹配。选择升级AndroidStudio或者降低NDK版本即可。
重新下载21.3.6528147版本,配置NDK通过,rebuild项目通过,问题解决, 最新版的 Android Studio 降级到 Android Studio 4.2.1 版本,NDK 降级到 21.3.6528147;
问题:clang++.exe: error: unknown argument: ‘-static-openmp‘
NDK版本!clang++: error: unknown argument: ‘-static-openmp‘
Android res\values-v26\values-v26.xml:9:5-12:13: AAPT: error: resource android:attr/colorError not f
解决方法:
compileSdkVersion 设置为28
Android res\values-v26\values-v26.xml:9:5-12:13: AAPT: error: resource android:attr/colorError not f
2 files found with path ‘lib/arm64-v8a/xxx.so‘ 问题
最后在自己的 nativelib module
的 build.gradle
的 android{}
加上
sourceSets {main {jniLibs.srcDirs = ['libs']}}
2 files found with path ‘lib/arm64-v8a/xxx.so‘ 问题
undefined reference to `cv::String::deallocate()一种可能解决方案
解决方案:替换库文件时候,同步替换头文件
undefined reference to `cv::String::deallocate()一种可能解决方案
关于解决gradle版本与gradle插件版本不一致问题的方法之一
关于解决gradle版本与gradle插件版本不一致问题的方法之一
Algorithm HmacPBESHA256 not available
在 D:\Android\sdk\.android
的目录下找到文件 debug.keystore
, 其实不缺少签名文件,这个问题,应该是之前安装了多个版本的 Android Studio,导致签名文件被覆盖了, 删除 debug.keystore
文件,重新签名即可。
JNI GetFieldID和GetMethodID函数解释及方法签名
GetFieldID
是得到java
类中的参数ID
,GetMethodID
得到java
类中方法的ID
,它们只能调用类中声明为public
的参数或方法。
举例说明:
jclass c = (*env)->FindClass(env,"com/camera/webcam/Test");
jfieldID width_id = (*env)->GetFieldID(env, c, "width", "I");
第一个参数:JNI接口对象;第二个参数:Java类对象;第三个参数:参数名(或方法名);第四个参数:该参数(或方法)的签名。
- 方法签名
调用 JNI
的 GetMethodID
函数获取一个 jmethodID
时,需要传入一个方法名称和方法签名,方法名称就是在 Java
中定义的方法名,方法签名的格式为:(形参参数类型列表)返回值。
JNI GetFieldID和GetMethodID函数解释及方法签名
如何从JNI返回多个数组到Java?
当您希望从一个函数返回多个“东西”时,您有两个选项(这并不是 JNI
特有的):要么创建一个包含所有结果的包装器对象(在您的例子中,是一个包含3个数组字段的 Java
类),要么使用 out
参数。也许,在您的情况下,如果您知道调用前的长度,后者可能会更容易一些。
所以,在 Java
中,您可以编写如下内容
package p;public class C {public void f() {byte[] array1 = new byte[10];int[] array2 = new int[20];String[] array3 = new String[5];fillArrays(array1, array2, array3);}native void fillArrays(byte[] byteArray, int[] intArray, String[] stringArray);
}
现在,在 C
中,这看起来是这样的:
JNIEXPORT void JNICALL
Java_p_C_fillArrays(JNIEnv *env, jobject thisC, jbyteArray byteArray, jintArray intArray, jobjectArray stringArray)
{jboolean isCopy;jint i = 0;char* names[] = {"one", "two", "three"};jbyte *c_byteArray = (*env)->GetByteArrayElements(env, byteArray, &isCopy);for (i=0; i<(*env)->GetArrayLength(env, byteArray); i++) {c_byteArray[i] = (jbyte)i;}(*env)->ReleaseByteArrayElements(env, byteArray, c_byteArray, 0);jint *c_intArray = (*env)->GetIntArrayElements(env, intArray, &isCopy);for (i=0; i<(*env)->GetArrayLength(env, intArray); i++) {c_intArray[i] = i;}(*env)->ReleaseIntArrayElements(env, intArray, c_intArray, 0);for (i=0; i<(*env)->GetArrayLength(env, stringArray) && i<sizeof(names)/sizeof(names[0]); i++) {(*env)->SetObjectArrayElement(env, stringArray, i, (*env)->NewStringUTF(env, names[i]));}
}
如何从JNI返回多个数组到Java?
Java: JNI对数组赋值并返回给Java
在 JNI
中对 Java
层的数组赋值有两种方式:一是在 Java
层创建好数组,然后传递到 JNI
层,由 JNI
层进行赋值;二是直接在 JNI
层创建好数组并赋值,然后返回数组到 Java
层。下面是两种方式的对比实现:
创建两个 native
方法
//传递数组,操作后,返回public native void passArrayMethod(int[] arr);//创建指定长度数组public native int[] createArrayMethod(int len);
生成对应的 C
函数
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_passArrayMethod(JNIEnv *, jobject, jintArray);JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod(JNIEnv *, jobject, jint);
传递数组给 JNI
,修改第一个元素值,然后排序
int com(const void *a, const void *b){return *(int *)a - *(int *)b;//升序
}
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_passArrayMethod(JNIEnv *env, jobject jobj, jintArray jarr){//1.获取数组指针jint *arr = env->GetIntArrayElements(jarr, NULL);*arr = 100;//2.获取数组长度int len = env->GetArrayLength(jarr);//3.排序qsort(arr, len, sizeof(jint), com);//4.释放资源env->ReleaseIntArrayElements(jarr, arr, JNI_COMMIT);
// env->ReleaseIntArrayElements(jarr, arr, JNI_ABORT);// 对于最后一个参数(如果指针指向的数组为副本时,否则该参数不起作用)// 0 copy back the content and free the elems buffer// JNI_COMMIT copy back the content but do not free the elems buffer// JNI_ABORT free the buffer without copying back the possible changes
};
JNI
生成数组,并返回
JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod(JNIEnv *env, jobject jobj, jint len){//1.新建长度len数组jintArray jarr = env->NewIntArray(len);//2.获取数组指针jint *arr = env->GetIntArrayElements(jarr, NULL);//3.赋值int i = 0;for(; i < len; i++){arr[i] = i;}//4.释放资源env->ReleaseIntArrayElements(jarr, arr, 0);//5.返回数组return jarr;
};
MainActivity
中调用
int[] arr = {1, 3, 2, 6, 8, 0};Log.i(TAG, "arr修改前: " + getArrayString(arr));jd.passArrayMethod(arr);Log.i(TAG, "arr修改后: " + getArrayString(arr));Log.i(TAG, "------------------------------------------");int[] arr_new = jd.createArrayMethod(10);Log.i(TAG, "arr_new: "+ getArrayString(arr_new) );
输出结果:
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr修改前: ,1,3,2,6,8,0
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr修改后: ,0,2,3,6,8,100
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: ------------------------------------------
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr_new: ,0,1,2,3,4,5,6,7,8,9
Java: JNI对数组赋值并返回给Java
[JNI 编程上手指南之 JNIEnv 详解]
- JNIEnv 是什么?
JNIEnv
即 Java Native Interface Environment
,Java
本地编程接口环境。JNIEnv
内部定义了很多函数用于简化我们的 JNI
编程。
JNI
把 Java
中的所有对象或者对象数组当作一个 C
指针传递到本地方法中,这个指针指向 JVM
中的内部数据结构(对象用 jobject
来表示,而对象数组用 jobjectArray
或者具体是基本类型数组),而内部的数据结构在内存中的存储方式是不可见的。只能从 JNIEnv
指针指向的函数表中选择合适的 JNI
函数来操作 JVM
中的数据结构。
在 C
语言中,JNIEnv
是一个指向 JNINativeInterface_
结构体的指针:
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv; // C 语言
#endifstruct JNINativeInterface_ {void *reserved0;void *reserved1;void *reserved2;void *reserved3;jint (JNICALL *GetVersion)(JNIEnv *env);jclass (JNICALL *DefineClass)(JNIEnv *env, const char *name, jobject loader, const jbyte *buf,jsize len);jstring (JNICALL *NewStringUTF)(JNIEnv *env, const char *utf);//省略其他函数指针//......
}
JNINativeInterface_
结构体中定义了非常多的函数指针,这些函数用于简化我们的 JNI
编程。C
语言中,JNIEnv
中函数的使用方式如下:
JNIEnv * env
// env 的实际类型是 JNINativeInterface_**
(*env)->NewStringUTF(env,"Hello from JNI !");
在 C++
代码中,JNIEnv
是一个 JNIEnv_
结构体:
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endifstruct JNIEnv_ {const struct JNINativeInterface_ *functions;
#ifdef __cplusplusjint GetVersion() {return functions->GetVersion(this);}jclass DefineClass(const char *name, jobject loader, const jbyte *buf,jsize len) {return functions->DefineClass(this, name, loader, buf, len);}jclass FindClass(const char *name) {return functions->FindClass(this, name);}jmethodID FromReflectedMethod(jobject method) {return functions->FromReflectedMethod(this,method);}jfieldID FromReflectedField(jobject field) {return functions->FromReflectedField(this,field);}jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {return functions->ToReflectedMethod(this, cls, methodID, isStatic);}jclass GetSuperclass(jclass sub) {return functions->GetSuperclass(this, sub);}//省略其他函数//......
}
JNIEnv_
结构体中同样定义了非常多的成员函数,这些函数用于简化我们的 JNI
编程。C++
语言中,JNIEnv
中函数的使用方式如下:
//JNIEnv * env
// env 的实际类型是 JNIEnv_*
env->NewstringUTF ( "Hello from JNI ! ");
更多详情见: JNI 编程上手指南之 JNIEnv 详解
Android NDK开发:JNI实战篇
java调用本地方法–JNI访问List集合
JNI系列(四)JAVA数据类型和JNI类型对照表
【参考】
Android Studio配置OpenCV的JNI接口
【Android+OpenCV】Android Studio的安装全过程+在Android Studio中配置OpenCV-重点关注
NDK开发遇到的三个错误:‘javah’ 不是内部或外部命令,编码GBK的不可映射字符, 程序包XX.XX不存在
OpenCV 在 Android Studio 的使用教程
Android Studio使用OpenCV进行图像基本处理
Android学习笔记之——基于Android的opencv开发(Android studio3.6+opencv4.3.0开发环境搭建)
解决办法:Invalid Gradle JDK configuration found
NDK版本!clang++: error: unknown argument: ‘-static-openmp‘
问题:clang++.exe: error: unknown argument: ‘-static-openmp‘
Android res\values-v26\values-v26.xml:9:5-12:13: AAPT: error: resource android:attr/colorError not f
JNI GetFieldID和GetMethodID函数解释及方法签名
如何从JNI返回多个数组到Java?
Java: JNI对数组赋值并返回给Java
JNI 编程上手指南之 JNIEnv 详解
Android NDK开发:JNI实战篇
java调用本地方法–JNI访问List集合
JNI系列(四)JAVA数据类型和JNI类型对照表