jni问题摘记

JNI函数的命名规则: Java代码中的函数声明需要添加native 关键 字;Native的对应函数名要以“Java_”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“_” 分割,在package名中的“.”也要改为“_”。


在JNI的Native函数中,其前两个参数JNIEnv *和jobject 是必需的——前者是一个JNIEnv 结构体的指针,这个结构体中定义了很多JNI的接口函数指针,使开发者可以使用JNI所定义的接口功能;后者指代的是调用这个JNI函数的Java对象,有点类似于C++中的this 指针。
在上述两个参数之后,还需要根据Java端的函数声明依次对应添加参数。





实际上JNI中的局部引用和C语言中局部变量是不同的,他的有效期是当前Native函数被调用的上下文中。我理解的调用上下文,为Java虚拟机的调用流程。Native函数是被Java虚拟机调用的,Native函数执行完成之后,控制流程将继续返回给Java虚拟机。局部变量在Native函数中,由Native代码调用Java虚拟机的JNI接口创建,秉着谁创建谁销毁的原则,当Native函数执行完成之后,如果局部引用没有被Native代码显示删除,那么局部引用在Java虚拟机中还是有效的。Java虚拟机来决定在什么时候来删除这个对象。这和C语言的局部变量概念是不同的。这也可以解释为什么Natvie函数能够以一个局部引用为返回值了。


局部引用在Native代码显示释放非常重要。

(如果你实现的Native函数是工具函数,会被频繁的调用。如果你在Native函数中没有显示删除局部引用,那么每次调用该函数Java虚拟机都会创建一个新的局部引用,造成局部引用过多。尤其是该函数在Native代码中被频繁调用,代码的控制权没有交还给Java虚拟机,所以Java虚拟机根本没有机会释放这些局部变量。)



c和c++的调用是不同的,
*env  和env

另外,折磨了一个下午,实验中不断出问题,搞清楚了,c和c++在编写native函数的时候也是不同的,c函数只要按照规则编写就可可以了。
改成cpp之后,怎么样都调用不了,看到网上不少资料都写到了要生成h文件,以及其中的关键字等等,想想是不是c不同呢,结果换成了c问题解决。



jnI问题

2。 如何将java传入的String参数转换为c的char*,然后使用?
java传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);

3. 将c中获取的一个char*的buffer传递给java?
这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’’就截断了。
有两种方式传递得到的数据:
一种是在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:
class RetObj
{
public byte[] bytearray;
}
这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下:
jclass cls;
jfieldID fid;
jbyteArray bytearray;
bytearray = (*env)->NewByteArray(env,len);
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);
cls = (*env)->GetObjectClass(env, retobj);
fid = (*env)->GetFieldID(env, cls, "retbytes", "[B"]);
(*env)->SetObjectField(env, retobj, fid, bytearray);

4。 不知道占用多少空间的buffer,如何传递出去呢?
在jni的c文件中new出空间,传递出去。java的数据不初始化,指向传递出去的空间即可。
四.JNI中对JAVA传入数据的处理
1. 如果传入的是bytearray的话,作如下处理得到buffer:
char *tmpdata = (char*)(*env)->GetByteArrayElements(env, bytearray, NULL);
(*env)->ReleaseByteArrayElements(env, bytearray, tmpdata, 0);
五.JAVA调用本地接口后的数据处理问题
1. java得到的数据是String的话,直接处理就可以了。
2. 得到的如果是bytearray的话,作如下处理:
DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytearray));
byte []byte1 = new byte[36];
in.read(byte1,0,36);
String string = new String(byte1);
System.out.println("读出的第一个字段为:"+string);

你可能感兴趣的:(jni问题摘记)