IDEA+Clion JNI开发记录

最近由于项目需要,接触了解了一部分jni相关开发,也踩了很多坑,包括结构体指针的使用以及c和java中传递,以及怎么把java的实体类转为c的结构体进而赋值返回。记录一下方便后续查阅。
开发工具使用的idea和clion,idea主要用来编写java相关代码并生成头文件,clion则用来开发c相关代码以及生成.so库文件。
环境配置不做过多记录,先从idea(java代码)部分开始。
1.新建项目

2.java代码
`public class JNIEntry {
static {
System.load("");
}

public native long init(String netModelPath,
                        String transModelPath,
                        String binModelPath,
                        String norModelPath);

public native Result recognize(long server,
                               byte[] wav,
                               int dataLength);

public static void main(String[] args) {
    JNIEntry entry = new JNIEntry();
    System.out.printf("");
}

}`
3.编译java代码生成头文件
配置一个idea中jni头文件生成工具,然后右击java类使用即可

4.clion中新建项目并使用cmake打包
我需要输出动态库,如下创建项目

项目目录结构:

5.实现java中定义的两个方法:
`JNIEXPORT jlong JNICALL Java_JNIEntry_init(
JNIEnv *env, jobject obc, jstring netPath, jstring transPath,
jstring binPath, jstring norPath) {

pvpr_instance_t bio = NULL;
char* net_model = jstringToChar(env, netPath);
char* trans_model = jstringToChar(env, transPath);
char* bin_model = jstringToChar(env, binPath);
char* nor_model = jstringToChar(env, norPath);
//初始化服务实例
bio = create_pvpr_space(net_model,trans_model,bin_model, nor_model);
if (NULL == bio) {
    fprintf(stderr, "create vpr space error!\n");
} else {
    fprintf(stderr, "create vpr space success!\n");
}

pvpr_instance_t *pvpr = NULL;
pvpr = &bio;
**jlong* result = (jlong *) pvpr;**
return *result;

}`

注:*这有一个坑卡了好久,服务句柄是以结构体指针表示,如何在java中拿到这个句柄并且反复作为参数传递使用呢?
把struct *整个转为jlong在c和java中交互

`JNIEXPORT jobject JNICALL Java_com_pachira_shared_identify_pvpr_core_PVPRIdentifierCore_recognize
(JNIEnv * env, jobject obj, jlong server, jbyteArray wav, jint length_wav) {
pvpr_instance_t bio;
bio = (pvpr_instance_t) server;
if (bio == NULL) {
fprintf(stderr,"invalid PachiraVpr server, please check\n");
return NULL;
}
char *data = JByteArrayToChars(env, wav);
pvpr_result_t result;
//辨认数据
result = pvpr_recognize_data(bio, data, (int)length_wav);

//资源释放
if(data != NULL) {
    free(data);
}
return convertJavaResult(env, result);

}`

注:这里也有一个坑的点,怎么把java定义的结果类作为c中的返回值返回
`//C result转换为java结果类
jobject convertJavaResult(JNIEnv* env, pvpr_result_t cResult) {
jclass objectClass = env->FindClass(pvpr_result_class);
//构造结果类
jmethodID methodId =
env->GetMethodID(objectClass, "", "()V");
jobject jResult = env->NewObject(objectClass, methodId);

//取出字段
jfieldID uid = env->GetFieldID(objectClass, "uid", "Ljava/lang/String;");
jfieldID score = env->GetFieldID(objectClass, "score", "F");
jfieldID confidence = env->GetFieldID(objectClass, "confidence", "F");

//赋值
env->SetFloatField(jResult, score, cResult->score);
env->SetFloatField(jResult, confidence, cResult->confidence);
env->SetObjectField(jResult, uid, charArray2Jstring(env,cResult->uid));
return jResult;

}`
在c中构造结果的结构体,然后赋值给对应属性最后返回

6.使用cmake编译生成so库
cMakeList文件配置如下:
cmake_minimum_required(VERSION 3.15) //设置项目名称 project(pvpr) //设置版本 set(CMAKE_CXX_STANDARD 14) //链接头文件内容 include_directories(header) //这里我引用了一个外部so库,使用绝对路径即可 link_libraries("xxxx/lib/vpr.so") //生成目标so库 指定库名,类型(动态库还是静态库),实现cpp文件,以及jni头文件 add_library(jniTest SHARED JniEntry.cpp header/JniEntry.h)
直接在clion中run即可,编译通过会生成so文件

7.在java中使用即可
这里我使用load加载可传输可配置的绝对路径或者so文件相对路径。
loadLibray方法也可以,需要添加lib加载路径

posted @ 2020-07-05 19:20  轻天  阅读(1873)  评论(0编辑  收藏  举报