Jni:04.数组排序、缓存策略和异常处理

代码请看:10.JNI基础-缓存策略和异常处理

知识点:

1. 数组排序:

  • void qsort(void * _Base, size_t _NumOfElements, size_t _SizeOfElements, int(*_PtFuncCompare)(const void *, const void *)) :c中的排序方法
  • jxxx* = env->GetXXArrayElements(env,jarray,NULL);:将java层的数组转换成c层的数组(指针)。
  • (*env)->ReleaseXXArrayElements(env, jarray, xxArray, JNI_OK);:同步数组的数据给 java 数组 xxArray 并不是 jarray。
     	// JNI_OK == 0::既要同步数据给 jarray, 又要释放 intArray
     	// JNI_COMMIT == 1:会同步数据给 jarray, 但是不会释放 intArray
     	// JNI_ABORT == 2:不会同步数据给 jarray, 但是会释放 intArray
    

native层:

// 4. 定义一个 compare 的方法
int compare(const int* number1, const int* number2){
	return *number1 - *number2;
}

// 数组排序
JNIEXPORT void JNICALL Java_com_east_jni10_Sample_sort
(JNIEnv* env, jclass jclz, jintArray jarray){
	
	// 2. 获取 jintArray 的首地址
	jint* intArray = (*env)->GetIntArrayElements(env,jarray,NULL);

	// 3. 获取数组的长度
	int length = (*env)->GetArrayLength(env,jarray);

	// 1. 对 jarray 进行排序
	// void qsort(void * _Base, size_t _NumOfElements, size_t _SizeOfElements, int(*_PtFuncCompare)(const void *, const void *))
	// 第一个参数:void* 数组的首地址
	// 第二个参数:数组的长度
	// 第三个参数:数组元素数据类型的大小
	// 第四个参数:数组的一个比较方法指针(Comparable)
	qsort(intArray,length,sizeof(int),compare);

	// 同步数组的数据给 java 数组 intArray 并不是 jarray, 可以简单理解为 copy
	// JNI_OK == 0::既要同步数据给 jarray, 又要释放 intArray
	// JNI_COMMIT == 1:会同步数据给 jarray, 但是不会释放 intArray
	// JNI_ABORT == 2:不会同步数据给 jarray, 但是会释放 intArray

	(*env)->ReleaseIntArrayElements(env, jarray, intArray, JNI_OK);

}

2. 局部引用和全局引用

  • jobject j_str = (*env)->NewObject(env,str_clz,jmid):局部引用对象生成。
  • (*env)->DeleteLocalRef(env,j_str); :删除局部引用
  • jstring globalStr =(*env)->NewGlobalRef(env,value); :创建全局引用
  • (*env)->DeleteGlobalRef(env,globalStr):删除全局引用

2.1. 局部引用

// 局部引用
JNIEXPORT void JNICALL Java_com_east_jni10_Sample_localRef
(JNIEnv * env, jclass jclz){
	// 在 native 层构建的 Java 对象,不用了应该怎么管理(应该进行回收删除)
	// native 层开辟的内存由谁管理,你能开辟多大

	// 字符串截取,String 对象
	jclass str_clz = (*env)->FindClass(env,"java/lang/String");
	jmethodID jmid = (*env)->GetMethodID(env, str_clz, "<init>", "()V");
	jobject j_str = (*env)->NewObject(env,str_clz,jmid);

	// 还有 100 行代码

	// jobject 不要再使用了,要回收 javaGC 的源码
	(*env)->DeleteLocalRef(env,j_str);

	// 删除了就不能再使用了,C 和 C++ 都需要自己释放内存(静态开辟的不需要,动态开辟的需要)
}

2.2 全局引用


// 全局引用
jstring globalStr;

JNIEXPORT void JNICALL Java_com_east_jni10_Sample_saveGlobalRef
(JNIEnv * env, jclass j_clz, jstring value){
	// 保存全局引用,其它方法需要用到
	globalStr = (*env)->NewGlobalRef(env,value);

	// NewWeakGlobalRef (java 中的软引用很像)无法保证对象不为空
}

JNIEXPORT jstring JNICALL Java_com_east_jni10_Sample_getGlobalRef
(JNIEnv * env, jclass j_clz){
	return globalStr;
}

// 删除全局引用
JNIEXPORT void JNICALL Java_com_east_jni10_Sample_deleteGlobalRef
(JNIEnv * env, jclass j_clz){
	(*env)->DeleteGlobalRef(env,globalStr);
}

3. 缓存策略 static, native层有一大堆方法需要去获取 name 属性 // 初始化全局静态缓存

3.1 局部静态缓存

局部静态缓存:即使方法被重复调用也不会反复的给static修饰的字段赋值。

JNIEXPORT void JNICALL Java_com_east_jni10_Sample_staticLocalCache
(JNIEnv * env, jclass j_clz, jstring value){
	// name 属性,赋值操作
	static jfieldID jfid = NULL; // 局部静态缓存,即使方法被重复调用也不会反复的去获取 jfieldID
	
	if (jfid){
		printf("fieldID is not null\n");
	}
	else{
		jfid = (*env)->GetStaticFieldID(env, j_clz, "name", "Ljava/lang/String;");// 如果没有静态缓存 这个方法会被多次调用,会去反复的获取 jfieldID
	}

	(*env)->SetStaticObjectField(env,j_clz,jfid,value);
}

3.2 全局静态缓存

全局静态缓存:即使方法被重复调用也不会反复的给static修饰的字段赋值。

// 全局静态缓存
static jfieldID f_name_id = NULL;
static jfieldID f_name1_id = NULL;
static jfieldID f_name2_id = NULL;


JNIEXPORT void JNICALL Java_com_east_jni10_Sample_staticLocalCache
(JNIEnv * env, jclass j_clz, jstring value){
	// 因为是静态缓存,所以这个方法被反复调用,也不会反复的去获取 jfieldID
	(*env)->SetStaticObjectField(env,j_clz,f_name_id,value);
}


JNIEXPORT void JNICALL Java_com_east_jni10_Sample_initStaticCache
(JNIEnv *env, jclass j_clz){
	// 初始化全局静态缓存
	f_name_id = (*env)->GetStaticFieldID(env, j_clz, "name", "Ljava/lang/String;");
	f_name1_id = (*env)->GetStaticFieldID(env, j_clz, "name1", "Ljava/lang/String;");
	f_name2_id = (*env)->GetStaticFieldID(env, j_clz, "name2", "Ljava/lang/String;");
}

4. 异常处理(简单讲,C++异常)

  • jthrowable throwable = (*env)->ExceptionOccurred(env);:正在抛出一个异常的本地引用
  • (*env)->ExceptionClear(env);:清除异常
  • (*env)->Throw(env, throwable);:将ExceptionOccurred获取到的异常直接抛给java层
  • (*env)->ThrowNew(env, no_such_clz, "NoSuchFieldException name3");:抛出自己想抛出的异常

异常说明:

  1. ExceptionCheck 用于检测如果一个异常已经抛出,则该方法将会返回JNI_TRUE就是typedef定义为1的布尔值。

  2. ExceptionOccurred 获取正在抛出一个异常的本地引用,Native或Java层必须处理该异常,并返回一个jthrowable对象。

  3. ExceptionDescribe 主要用于打印出异常的错误描述。

  4. ExceptionClear 清除刚刚抛出的异常。

  5. FatalError 的作用比较特殊,产生一个致命性的错误,Android123提示这样会导致JVM将关闭,就是程序停止运行了,所以使用时要谨慎。

说明下:ExceptionCheck 用法

 env->FindClass("Android123CWJ"); //假设这个类本身不存在
  if(env->ExceptionCheck())
  {
    env->ExceptionDescribe();
    env->ExceptionClear();
  }

下面是:ExceptionOccurred 用法

注意: 保存后在抛出异常后记得 return

// 异常处理
JNIEXPORT void JNICALL Java_com_east_jni10_Sample_exception
(JNIEnv *env, jclass j_clz){

	// 假设现在想给 ,name3 赋值
	jfieldID jfid = (*env)->GetStaticFieldID(env, j_clz, "name3", "Ljava/lang/String;");

	// 两种方式解决
	// 1. 补救措施, 不拿 name3 拿 name
	// 1.1 有没有异常
	jthrowable throwable = (*env)->ExceptionOccurred(env);
	/*if (throwable){
		// 补救措施,先把异常清除
		printf("native have a exception");
		// 清除异常
		(*env)->ExceptionClear(env);
		// 重新获取 name 属性
		jfid = (*env)->GetStaticFieldID(env, j_clz, "name", "Ljava/lang/String;");
	}*/

	// 2. 想给 java 层抛一个异常
	if (throwable){
		// 清除异常
		(*env)->ExceptionClear(env);
		// 给 java 层抛 一个 Throwable 异常

		// 第一种方式,直接把异常抛给 java 层
		(*env)->Throw(env, throwable);

		// 第二种方式抛异常
		// (*env)->ThrowNew(env, no_such_clz, "NoSuchFieldException name3");

		return; //  必须 return 如果不的话,程序会接着往下运行,肯定还会crash
	}

	jstring name = (*env)->NewStringUTF(env, "Eastrise");
	(*env)->SetStaticObjectField(env, j_clz, jfid, name);

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值