最近在JNI的项目,由于第一次使用,查了一些资料,现总结如下:
参考资料:
1、http://docs.oracle.com/javase/6/docs/technotes/guides/jni/index.html
2、http://blog.csdn.net/fightspirite/article/details/6859829
3、http://blog.csdn.net/super005/article/details/6387575
4、http://www.cr173.com/html/17867_1.html
5、http://www.cnblogs.com/codc-5117/archive/2012/09/06/2672815.html
6、http://www.cnblogs.com/codc-5117/archive/2012/09/06/2672833.html
7、http://blog.chinaunix.net/uid-23065002-id-3077584.html
8、http://blog.csdn.net/zgyulongfei/article/details/7409441
总结一:关于JNIEnv和JavaVM
native程序中频繁使用JNIEnv*和JavaVM*。而C和C++代码使用JNIEnv*和JavaVM*这两个指针的做法是有区别的,网上大部分代码都使用C++,基本上找不到关于C和C++在这个问题上的详细叙述。
在C中:
使用JNIEnv* env要这样 (*env)->方法名(env,参数列表)
使用JavaVM* vm要这样 (*vm)->方法名(vm,参数列表)
在C++中:
使用JNIEnv* env要这样 env->方法名(参数列表)
使用JavaVM* vm要这样 vm->方法名(参数列表)
上面这二者的区别是,在C中必须先对env和vm间接寻址(得到的内容仍然是一个指针),在调用方法时要将env或vm传入作为第一个参数。C++则直接利用env和vm指针调用其成员。
总结二:"char *"与"java string"的互相转换
1.jstring转换为C风格字符串
char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);
使用完毕后,应调用:
(*env)->ReleaseStringUTFChars(env,jstring, test);
释放资源。
2.C风格字符串转换为jstring
char charStr[50];
jstring jstr;
jstr = env ->NewStringUTF(charStr);
3.C语言中获取的一段char*的buffer传递给Java
在jni中new一个byte数组,然后使用
(*env)->SetByteArrayRegion(env,bytearray, 0, len, buffer)
操作将buffer拷贝到数组中。
这种方式主要是针对buffer中存在“\0”的情况,如果以C风格字符串的方式读入,就会损失“\0”之后的字符。
4.数组操作
数组操作的相关函数列表如下:
JNI函数 功能
GetArrayLength 返回数组中的元素数
NewObjectArray 创建一个指定长度的原始数据类型数组
GetObjectArrayElement 返回Object数组的元素
SetObjectArrayElement 设置Object数组的元素
GetObjectArrayRegion 将原始数据类型数组中的内容拷贝到预先分配好的内存缓存中
SetObjectArrayRegion 设置缓存中数组的值
ReleaseObjectArrayRegion 释放GetObjectArrayRegion分配的内存
对int,char等基本数据类型的数组操作,将相关Object名称替换为对应基本数据类型名称即为相关函数。
数组操作的方法选择基于使用者的需求而定,如果使用者需要在内存中拷贝数组并对其进行操作那么一般使用GetObjectArrayRegion和SetObjectArrayRegion函数,否则一般使用SetObjectArrayElement和GetObjectArrayElement函数。
写一个简单的例子,分享一下:
http://pan.baidu.com/s/11MThi
总结三:常用的字符串类型处理的函数:
(1) const char* GetStringUTFChars (jstring string,jboolean* isCopy)
返回指向字符串UTF编码的指针,如果不能创建这个字符数组,返回null。这个指针在调用ReleaseStringUTFChar()函数之前一直有效。 参数:
string Java字符串对象
isCopy 如果进行拷贝,指向以JNI_TRUE填充的jboolean,否则指向以JNI_FALSE填充的jboolean。
(2) void ReleaseStringUTFChars(jstring str, const char* chars)
通知虚拟机本地代码不再需要通过chars访问Java字符串。
参数:
string Java字符串对象
chars 由GetStringChars返回的指针
(3) jstring NewStringUTF(const char *utf)
返回一个新的Java字符串并将utf内容拷贝入新串,如果不能创建字符串对象,返回null。通常在反值类型为string型时用到。
参数:
utf UTF编码的字符串指针,对于数值型参数,在C/C++中可直接使用
参数:
array 数组对象
isCopy 如果进行拷贝,指向以JNI_TRUE填充的jboolean,否则指向以JNI_FALSE填充的jboolean。
例如:jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy)
(4) void ReleaseXxxArrayElements(xxxArray array,xxx *elems, jint mode)
通知虚拟机不再需要从GetXxxArrayElements得到的指针。
参数:
array 数组对象
elems 不再需要的指向数组元素的指针
mode 0=在更新数组元素后释放elems缓冲器
JNI_COMMIT=在更新数组元素后不释放elems缓冲器
JNI_ABORT=不更新数组元素释放elems缓冲器
例如:void ReleaseBooleanArrayElements(jbooleanArray array,jboolean *elems, jint mode)
(5) xxxArray NewXxxArray(jsize len)
产生一个新的数组,通常在反值类型为数组型时用到。
参数:
len 数组中元素的个数。
例如:jbooleanArray NewBooleanArray(jsize len)
总结九:JNI内存泄露
一:
在c++中new的对象,如果不返回java,必须用release掉,否则内存泄露。包括NewStringUTF,NewObject等。
如果返回java不必release,java会自己回收。
...
jstring jstr = env->NewStringUTF((*p).sess_id);
...
env->DeleteLocalRef( jstr); /* 必须调用,否则泄露 */
...
jobject jobj = env->NewObject(clazz,midInit); /* 有返回,不用管 */
return jobj;
二:
jstring jstr = (jstring)env->CallObjectMethod(authenRequest, mid_authenReq_getSdId_S);
env->GetStringUTFRegion(jstr,0,env->GetStringLength(jstr),authenReq.sd_id);
当jstr是null时,env->GetStringLength(jstr)会出错,导致jvm崩溃
三:
在 JNI 编程时,正确控制 JNI Local Reference 的生命期。
如果需要创建过多的 Local Reference,那么在对被引用的 Java 对象操作结束后,需要调用 JNI function(如 DeleteLocalRef()),及时将 JNI Local Reference 从 Local Ref 表中删除,以避免潜在的内存泄漏。