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

来源:互联网 发布:男士衣服搭配 知乎 编辑:程序博客网 时间:2024/06/08 17:49

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 invalidException 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();    }