JNI中修改(基本类型)参数并返回到Java层使用

  最近在JNI相关项目中遇到一个问题:在Java层传入多个int类型的参数,在jni层修改参数值或地址之后重新返回到Java层。这应该算是基本知识了,尤其是基本类型的参数往往看似简单,所以在之前学习jni时就一笔带过了,结果现在突然遇到这个问题竟然需要再查找资料学习,因此这周在重新复习一遍基础知识之后将此记录一下。走的再远,也不要忘记脚底的路。

  还是用Demo来解释下需求和对应解决方案吧

 1 public class LibraryManager {
 2 
 3     static{
 4         System.loadLibrary("libtest");
 5     }
 6 
 7     public final static native int add1(int arg1, int arg2, int result);
 8     public final static native int add2(int arg1, int arg2, int result);
 9   
12 }

 

  在Java层写了两个方法分别模拟这个需求,在底层对arg1arg2参数做操作,之后将结果存入result中,希望能在Java层使用result,至于方法的返回值,则是模拟表示方法执行成功与否的标志位。

下面分别在jni中用两种方式实现该需求,当然这两种都是典型错误的。

 1 JNIEXPORT jint JNICALL Java_com_xxx_LibraryManager_add1(
 2 JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2, jint jarg3){
 3     jarg3=jarg1+jarg2;
 4     LOGI("add1 arg3=%d", jarg3);
 5     return 1;
 6 }
 7 
 8 JNIEXPORT jint JNICALL Java_com_xxx_LibraryManager_add2(
 9 JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2, jint *jarg3){
10     int result=jarg1+jarg2;
11     jarg3=&result;
12     LOGI("add2 arg3=%d", *jarg3);
13     return 2;
14 }

 

  如果还记得jni的运行原理的话,应该很容易理解这么写只是修改在c线程里边的参数值(add1()中)和参数地址(add2()中),至于Java层对应的参数没有变化,也就是说jni中的基本类型作为参数时只是形参传入的,对于上层没有任何影响。当然如果作为return值的话是绝对可以的,但是现在讨论的是作为参数值的方法。那么还是这个需求,应该怎么解决呢?

  在LibraryManager.class中增加两个新的方法

1     public final static native int addConfirm1(int arg1, int arg2, int[] result);
2     public final static native int addConfirm2(int arg1, int arg2, Integer result);

 

看第三个参数就能知道解决方法了,将int类型转换为int[]addConfirm1()中)和int对应的整型类(addConfirm2()中),当然就是使用jni中的JNIEnv可以获取到的方法来修改参数,至于具体用法在下面详细列出。

 1 JNIEXPORT jint JNICALL Java_com_bob_testlib_LibraryManager_addConfirm1(
 2 JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2, jintArray jarg3){
 3     int result=jarg1+jarg2;
 4     int *arg3 = jenv->GetIntArrayElements(jarg3, 0);
 5     *arg3=result;
 6     jenv->ReleaseIntArrayElements(jarg3, arg3, 0);
 7     return 3;
 8 }
 9 JNIEXPORT jint JNICALL Java_com_bob_testlib_LibraryManager_addConfirm2(
10 JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2, jobject jarg3){
11     int result=jarg1+jarg2;
12     jclass intClass = jenv->FindClass("java/lang/Integer");
13     jfieldID intId = jenv->GetFieldID(intClass, "value", "I");
14     jenv->SetIntField(jarg3, intId, result);
15     return 4;
16 }

 

  在addConfirm1()中将int[]的参数传入,这样可以通过JNIEnvGetIntArrayElements()获取到传入参数的地址并绑定到int*变量中,在修改变量之后,通过ReleaseIntArrayElements()通过第三个参数mode=0更新JavajintArray的参数,并释放JNI层的int*变量。

  在addConfirm2()中将jobject参数传入,通过JNIEnvFindClass()找到JavaInteger类对应jni层的jclass,再根据jclass通过JNIEnvGetFiledID()找到JavaInteger类的value对应jni层的jclassjfieldID,最后通过JNIEnvSetIntField()将要更新的int值存入到Java层的jobject中即可。这个流程就是把Java层的Integer看成自定义的类,之后就是更新自定义类中的变量。

  目前能想到的有上面两种方式可以解决类似需求,其思想都是将Java中的基本类型转变成Java类,之后再用JNI中对类的操作方法进行修改。

 

posted on 2019-06-29 21:53  白少木丿  阅读(904)  评论(0编辑  收藏  举报

导航