Java通过JNI调用C 传递参数时,想传递地址,怎么实现

sscust 发布于 2014/07/25 09:18
阅读 5K+
收藏 1

求大神指教,在Java调用C时该如何传递数组的地址,可以在方法里直接对数组操作,而不是操作一个复制来的一个副本???

1.Value.java

public class Value{
    public native void changeValue(int a);
    public native void changeAddr(int a[]);
    public native void changeArray(int a[],int b[]);
}

2.通过javah编译成的Value.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Value */


#ifndef _Included_Value
#define _Included_Value
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Value
 * Method:    changeValue
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_Value_changeValue
  (JNIEnv *, jobject, jint);


/*
 * Class:     Value
 * Method:    changeAddr
 * Signature: ([I)V
 */
JNIEXPORT void JNICALL Java_Value_changeAddr
  (JNIEnv *, jobject, jintArray);


/*
 * Class:     Value
 * Method:    changeArray
 * Signature: ([I[I)V
 */
JNIEXPORT void JNICALL Java_Value_changeArray
  (JNIEnv *, jobject, jintArray, jintArray);


#ifdef __cplusplus
}
#endif
#endif


4.函数在C中的实现部分

JNIEXPORT void JNICALL Java_Value_changeAddr
(JNIEnv * env, jobject jo, jintArray jarr){
    int i;
    jint* a1=env->GetIntArrayElements(jarr,0);
    int arrLen=env->GetArrayLength(jarr);
    int temp;
    for (i=0;i<arrLen/2;i++)//将jarr数组中的元素调转顺序
    {
        temp=a1[i];
        a1[i]=a1[arrLen-1-i];
        a1[arrLen-1-i]=temp;
    }
    for (i=0;i<arrLen;i++)
    {
        printf("%d",a1[i]);
        printf("\t");
    }
     printf("\n");

}
JNIEXPORT void JNICALL Java_Value_changeArray
(JNIEnv * env, jobject jo, jintArray jarr1, jintArray jarr2){
    jint *a1,*a2;int i;
    a1=env->GetIntArrayElements(jarr1,0);//获取数组jarr1的首地址(指针)
    a2=env->GetIntArrayElements(jarr2,0);
    int len1=env->GetArrayLength(jarr1);
    int len2=env->GetArrayLength(jarr2);
    for (i=0;i<len1;i++)
    {
        printf("%d",a1[i]);
        printf("\t");
    }
    printf("\n");
    for (i=0;i<len2;i++)
    {
        printf("%d",a2[i]);
        printf("\t");
    }

}


5.java的main函数调用

public class Test{
public static void main(String args[]){
    System.loadLibrary("value");
    Value v=new Value();
    int a[]={1,2,3,4};
    int b[]={6,7,8,9};
    v.changeAddr(a);
    v.changeArray(a,b);/*数组a在传递到changeAddr(int a[])居然只是传了一个副本???传地址不是改变数组中的内容吗??*/     

    }

}

6.结果

4       3       2       1
1       2       3       4
6       7       8       9

7.问题就出在第二行的输出为什么数组中元素没有发生变化???求大神指教,在Java调用C时该如何传递数组的地址,在方法里直接对数组操作,而不是操作一个复制来的一个副本???


加载中
0
obaniu
obaniu

java数组传递给jni实际是指针,只是java禁止你直接在jni里对java数组操作,所以在jni需要对java数组转换为jni里可以操作的数组。典型的还有字符串,c struct等。

之所以禁止jni直接操作数组可能是jvm在安全等方面的考虑。jni调用涉及到java内存栈和native内存栈。jvm只能处理到java内存,没有实现自动管理jni调用的内存。试想下如果jni里操作java数组刚好被回收了会怎样?

所以在jni里需要拷贝java数组副本,而且分配或者是引用了对象,需要手动释放删除(DeleteLocalRef、ReleaseXX)。jni调用实际是个很大的开销.


obaniu
obaniu
回复 @sscust : 用一个class包装参数
sscust
sscust
回复 @droidwolf : 谢谢,这我知道,我这个小程序只是做了个测试,其实要做的项目里,有三四个C 函数,而且,参数列表里以后10几个参数,绝大部分是指针型的,完全做不了靠一个return就完事的
obaniu
obaniu
回复 @sscust : 要修改数组,要用返回值的方法返回一个新的数组,比如上面的函数返回个新数组a1 jintArray JNICALL java_ValueChangeArray(.....){ ... return a1; }
sscust
sscust
谢谢,但是我觉得我可以去手打释放内存,总觉得Java给程序员的权限少了很多,捆着了手脚似的,没有C灵活,话说又回来,我这个如题的要求能实现吗??怎么实现?
返回顶部
顶部