JNI笔记 JNI引用,异常处理,初始化成员变量

时间:2021-08-18 09:36:24

1 回收局部引用变量
局部引用,通过DeleteLOcalREf手动释放对象

c代码

JNIEXPORT void JNICALL Java_com_yeliang_JniTest_localRef(JNIEnv *env, jobject jobj){
int i = 0;
//模拟:循环创建对象
for (;i<5;i++){
jclass cls = (*env)->FindClass(env,"java/util/Date");
jmethodID constructor_mid = (*env)->GetMethodID(env,cls,"<init>","()V");
//创建Date对象
jobject obj = (*env)->NewObject(env, cls, constructor_mid);

//不在使用jobject对象了
//通知垃圾回收期回收这些对象
(*env)->DeleteLocalRef(env,obj);
}
}

java代码

JniTest jniTest = new JniTest();

public static void main(String[] args) {
JniTest jniTest = new JniTest();
jniTest.localRef();
}

使用场景

  • 访问一个较大的java对象,使用完了之后,还要进行复杂的耗时操作
  • 创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性

2 回收局部引用变量
c代码

jstring global_str;

//创建
JNIEXPORT void JNICALL Java_com_yeliang_JniTest_createGlobalRef(JNIEnv *env, jobject jobj){
jstring obj = (*env)->NewStringUTF(env,"russul westbruk is so powerful!");
global_str = (*env)->NewGlobalRef(env,obj);
}

//获得
JNIEXPORT void JNICALL Java_com_yeliang_JniTest_getGlobalRef(JNIEnv *env, jobject jobj){
return global_str;
}

//释放
JNIEXPORT void JNICALL Java_com_yeliang_JniTest_deleteGlobalRef(JNIEnv *env, jobject jobj){
(*env)->DeleteGlobalRef(env,global_str);
}

java代码

public native void createGlobalRef();

public native String getGlobalRef();

public native void deleteGlobalRef();

public static void main(String[] args) {
JniTest jniTest = new JniTest();

jniTest.createGlobalRef();
System.out.println(jniTest.getGlobalRef());
jniTest.deleteGlobalRef();
}

打印结果

russul westbruk is so powerful!

3 异常处理

  • 保证Java代码可以运行
  • 补救措施保证C代码继续运行
  • JNI自己抛出的异常,在Java层无法捕捉,只能在C层清空
  • 用户通过TrowNew抛出的异常,可以在Java层捕捉

c代码

JNIEXPORT void JNICALL Java_com_yeliang_JniTest_exception(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env,jobj);
jfieldID fid = (*env)->GetFieldID(env,cls,"aa","Ljava/lang/String");
}

成员变量里的名字为field但是我这里写成了aa

public String field = "yeliang";

public static void main(String[] args) {
JniTest jniTest = new JniTest();

try{
jniTest.exception();
}catch(Exception e){
System.out.println("发生异常:"+e.getMessage());
}
jniTest.exception();
}

执行后报错

Exception in thread "main" java.lang.NoSuchFieldError: aa

c代码

JNIEXPORT void JNICALL Java_com_yeliang_JniTest_exception(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env,jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "aa", "Ljava/lang/String;");

//检测是否发生java异常
jthrowable exception = (*env)->ExceptionOccurred(env);
if (exception!=NULL){
//让java代码可以继续运行
(*env)->ExceptionClear(env);

//补救措施
fid = (*env)->GetFieldID(env, cls, "field", "Ljava/lang/String;");
}
//获取属性的值
jstring jstr = (*env)->GetObjectField(env,jobj,fid);
char *str = (*env)->GetStringUTFChars(env,jstr,NULL);

//对比属性值是否合法

// ==0相等
if (_stricmp(str, "give me the ball") != 0){
//认为抛出异常,给java层处理
jclass newexcls = (*env)->FindClass(env,"java/lang/IllegalArgumentException");
(*env)->ThrowNew(env,newexcls,"key's value is invalid");
}
}

打印结果

发生异常:field's value is invalid
Exception in thread "main" java.lang.IllegalArgumentException: field's value is invalid

虽然aa的成员变量没有找到,但是并没有抛出异常。因为把异常清空了。通过找到了名字对应的field的成员变量,并对比成员变量的值,在不相等的情况下手动抛出了一个异常。
打印的结果第一个是捕获的异常,第二个是程序直接crash。因为没有捕获。

4 缓存
c代码

JNIEXPORT void JNICALL Java_com_yeliang_JniTest_cache(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env,jobj);
//获取ffiedID只获取一次
//局部静态变量
static jfieldID key_id = NULL;
if (key_id==NULL){
key_id = (*env)->GetFieldID(env, cls, "field", "Ljava/lang/String;");
printf("========GetFieldID===========\n");
}


}

java代码

    public static void main(String[] args) {
JniTest jniTest = new JniTest();

jniTest.cache();
}

打印结果

========GetFieldID===========

可以看出,key_id 只初始化了一次。

5 初始化成员变量
初始化全局变量 动态库家在完成之后 立刻缓存起来
c代码

jfieldID field;
jmethodID random_mid;

JNIEXPORT void JNICALL Java_com_yeliang_JniTest_initIds(JNIEnv *env, jclass jcls){
field = (*env)->GetFieldID(env, jcls, "field", "Ljava/lang/String;");
random_mid = (*env)->GetMethodID(env,jcls,"getRandomInt","(I)I");
}

java代码

public String field = "yeliang";

public int getRandomInt(int range){
System.out.println("Java中的方法执行了");
return new Random().nextInt(range);
}

static{
System.loadLibrary("jni_study");
initIds();
}