一、JNI访问java数组
1.JNI访问数组并使用qsort快速排序
JNIEXPORT void JNICALL Java_com_Jni_1Test_giveArray (JNIEnv * env, jobject jobj, jintArray arr) { //申明排序用的比较函数 int compare(jint * a, jint *b); //java数组转换为JNI数组 jint *elemts = (*env)->GetIntArrayElements(env, arr, NULL); if (elemts == NULL){return;}//非空判断 //获取java数组长度 int len = (*env)->GetArrayLength(env, arr); //qsortC语言快速排序函数 //elemts:需要排序的数组 //len:数组长度 //sizeof(jint):数组元素的宽度 //compare:比较函数 qsort(elemts, len, sizeof(jint), compare); //JNI操作 释放可能的内存 将JNI修改的数据重新写回原来的内存 (*env)->ReleaseIntArrayElements(env, arr, elemts, JNI_COMMIT); } //C语言中 布尔型的定义:所有>0的都为true,<=0为false int compare(jint * a, jint *b) { return *a - *b; }
2.jni访问引用类型的数组
/* * java中定义JNI方法 * public native String[] initStringArray(int size); */ JNIEXPORT jobjectArray JNICALL Java_com_hubin_Jni_1Test_initStringArray (JNIEnv * env, jobject jobj, jint size) { //创建jobjectArray 数组 jobjectArray result; jclass jclz; int i; //反射找到对应java对象 jclz = (*env)->FindClass(env, "java/lang/String"); if (jclz == NULL) {return NULL;} result = (*env)->NewObjectArray(env, size, jclz, jobj); if (result == NULL) {return NULL;} //赋值 for (i = 0; i < size; i++) { //C字符串 char * c_str = (char *)malloc(256); memset(c_str, 0, 256); //将int 转换成为char sprintf(c_str, "hello num: %d\n", i); //C ->jstring jstring str = (*env)->NewStringUTF(env, c_str); if (str == NULL) {return NULL;} //将jstring赋值给数组 (*env)->SetObjectArrayElement(env, result, i, str); free(c_str); //释放 c_str = NULL; //(*env)->DeleteGlobalRef(env, str); } //返回jobjectArray return result; }
二、引用
- 1.局部引用
// 定义方式多样:FindClass,NewObject,GetObjectClass,NewCharArray.... NewLocalRef() 这些都是引用
//释放方式: 1 方法调用完JVM 会自动释放 2.DeleteLocalRef
// 不能在多线程里面使用
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_localRef
(JNIEnv * env, jobject jobj) {
int i = 0;
for (i = 0; i < 5; i++)
{
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID jmid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//创建一个Date类型的局部引用
jobject obj = (*env)->NewObject(env, cls, jmid);
//使用这个引用
//释放引用(JNI局部引用表里面最多存放512个引用,所以要及时释放避免引用溢出)
(*env)->DeleteLocalRef(env, cls);
(*env)->DeleteLocalRef(env, obj);
}
}
2.全局引用
/* * java中native方法 * 创建: public native void createGlobalRef(); * 获取: public native String getGlobalRef(); * 释放: public native void delGlobalRef(); */ //跨线程,跨方法使用 // NewGlobalRef 是创建全局引用的唯一方法 jstring global_str; JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_createGlobalRef (JNIEnv * env, jobject jobj) { jobject obj = (*env)->NewStringUTF(env, "JNI is intersting"); global_str = (*env)->NewGlobalRef(env, obj); //创建全局引用的唯一方法 } JNIEXPORT jstring JNICALL Java_com_hubin_Jni_1Test_getGlobalRef (JNIEnv * env, jobject jobj) { return global_str; } JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_delGlobalRef (JNIEnv * env, jobject jobj) { (*env)->DeleteGlobalRef(env, global_str); }
3.弱全局引用
//弱全局引用类(似于java中的弱引用) //它不会阻止GC,/跨线程,跨方法使用 jclass g_weak_cls; JNIEXPORT jstring JNICALL Java_com_hubin_Jni_1Test_createWeakRef (JNIEnv * env, jobject jobj) { jclass cls_string = (*env)->FindClass(env, "java/lang/String"); g_weak_cls = (*env)->NewWeakGlobalRef(env, cls_string); return g_weak_cls; }
三、JNI异常处理
1>JNI层检查并抛出异常
//JNI 异常处理(java中的try不能捕获JNI发生的异常)
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_exception
(JNIEnv * env, jobject jobj) {
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//检查是否发送异常
jthrowable ex = (*env)->ExceptionOccurred(env);
// 判断异常是否发生
if (ex != NULL) {
jclass newExc;
//清空JNI 产生的异常(清空掉异常以后java代码会继续往下执行)
(*env)->ExceptionClear(env);
//IllegalArgumentException 创建一个java层可以识别到的异常
newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if (newExc == NULL){
printf("exception\n");
return;
}
//抛出一个异常
(*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ");
}
}
2> java层捕获异常
try{
jni_Test.exception(); //调用JNI方法的地方
}catch(Exception e){
System.out.println(e.tostring());
}
四、缓存策略
1.局部静态变量进行缓存
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_cached (JNIEnv * env, jobject jobj) { jclass cls = (*env)->GetObjectClass(env, jobj); //static 存储在静态区 只需要定义和初始化一次 之后就只需直接使用 生命周期很长 static jfieldID fid = NULL; //静态的变量 if (fid == NULL) { fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); printf("GetFieldID\n"); } }
2.全局变量
jfieldID fid_glb; JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_cachedGlobal (JNIEnv * env, jclass cls) { if (fid_glb == NULL) { fid_glb = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); } }
3.缓存策略和弱引用联合使用带来的问题
1> C中代码(有问题的代码模拟)
//局部引用第二次调用的时候可能已经被释放 会产生野指针
JNIEXPORT jstring JNICALL Java_JniMain_AcessCacheNewString
(JNIEnv * env, jobject jobj) {
//定义一个静态的局部变量(尽量避免使用这种情况的使用)
static jclass cls_string = NULL;
if (cls_string == NULL)
{
printf("alvin in Java_JniMain_AcessCache_newString out: \n");
//给局部静态变量赋一个局部引用
cls_string = (*env)->FindClass(env, "com/hubin/jni/Test");
}
//使用这个静态局部变量
jmethodID jmid = (*env)->GetMethodID(env, cls_string, "getRef", "(I)I");
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex != NULL)
{
jclass newExc;
// 让java 继续运行
(*env)->ExceptionDescribe(env);//输出关于这个异常的描述
(*env)->ExceptionClear(env);
printf("C exceptions happend\n");
}
printf("alvin out Java_JniMain_AcessCache_newString\n");
return NULL;
}
JNIEXPORT jstring JNICALL Java_hubin_Jni_1Test_AcessCF
(JNIEnv * env, jobject jobj) {
//Sleep(100);
return Java_JniMain_AcessCacheNewString(env, jobj);
}
2> java中测试代码
for(int i= 0;i<100;i++){
jni_Test.AcessCF(); //多次调用jni方法
//大数组申请大量内存,触发GC
long[] ar = new long[1024*1024];
}