• 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中对类的操作方法进行修改。

  • 相关阅读:
    Go基础数据类型
    在foreach中使用distinct查找不重复记录
    DataTable,List去重复记录的方法(转载)
    ArcEngine的IMap接口(转载)
    根据Excel表格建立Shp文件(开发详解及源代码)(转载)
    axmapcontrol和mapcontrol有什么区别呢(转发)
    DataSet多表查询操作(转载)
    c#调用DLL(转载)
    wall 系列技术贴
    ArcEngine的IFeaturLayer接口(转载)
  • 原文地址:https://www.cnblogs.com/BobGo/p/11108019.html
Copyright © 2020-2023  润新知