NDK开发学习笔记(3):JNI访问数组、引用、异常处理、缓存策略

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011118524/article/details/77150919
/*
* jni访问java中的数组
*/
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_giveArray
(JNIEnv *env, jobject jobj, jintArray arr) {

    int compare(jint *a, jint *b);//声明方法,可以在函数的内部
    //jintArray->jint *
    jint *elemts = (*env)->GetIntArrayElements(env, arr, NULL);
    if (elemts == NULL) {
        return;
    }

    //获取数组的长度
    int len = (*env)->GetArrayLength(env, arr);
    qsort(elemts, len, sizeof(jint), compare);//c的库中提供的排序方法

    //释放可能的内存;将JNI修改的数据重新写回原来的内存,所以如果不调用此方法的话,java中的打印的数据还是没有排序的
    (*env)->ReleaseIntArrayElements(env, arr, elemts, JNI_COMMIT);
}

int compare(jint *a, jint *b) {
    return *a - *b;
}


/*
* 访问引用数据类型的数组
*通过jni实现一个数组并返回给java使用
*/
JNIEXPORT jobjectArray JNICALL Java_com_mei_test_jni_JniTest_initStringArray
(JNIEnv *env, jobject jobj, jint size ) {
    //1、创建jobjectArray
    jobjectArray result;
    jclass jclz;
    jint i;
    jclz = (*env)->FindClass(env, "java/lang/String");
    if (jclz == NULL) {
        return NULL;
    }
    result = (*env)->NewObjectArray(env, size, jclz, jobj);
    if (result == NULL) {
        return NULL;
    }
    //2、赋值
    for ( i = 0; i < size; i++)
    {
        //c字符串
        char * c_str = (char*)malloc(256);
        memset(c_str, 0, 256);
        //c语言,将int转换成为char
        sprintf(c_str, "hello num:%d\n", i);
        //C String->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);
    }
    //3、返回
    return result;
}

//JNI引用
//局部引用
//全局引用
//弱全局引用

/*
* 局部引用:定义方式多样
* 创建方式:有很多,如:FindClass,NewObject,GetObjectClass,NewCharArray....NewLocalRef()等等都是创建局部引用的方式。
* 产生了一个引用类型的操作或方法,都可以认为是创建了一个局部引用,所以FindClass也是一个创建局部引用的操作
* 释放局部引用的方式:1、方法调用完JVM会自动释放;2、通过DeleteLocalRef手动释放。
* 注意:局部引用不能在多线程中使用,
*/
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_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");
        //(*env)->NewObject(env, cls, jmid)这个就是创建了一个Date类型的局部引用
        jobject obj = (*env)->NewObject(env, cls, jmid);
        //使用这个引用

        //释放引用
        /*
        *在JNI中,会创建一个布局引用表,其大小为512,即能存储512个局部引用,如果我们没有及时动释放的话,
        *如果在某一时间创建的局部引用超过了512个,就会造成内存溢出。因此即使JVM会自动释放内存空间,我们还是要自己手动释放,避免内存溢出
        */
        (*env)->DeleteLocalRef(env, cls);
        (*env)->DeleteLocalRef(env, obj);
    }
}

/*
* 全部引用
* 创建方式:NewGlobalRef方法式创建全局引用的唯一方式
* 释放方式:通过DeleteGlobalRef方法释放全局引用
* 优点:可以跨线程,垮方法使用
*/
jstring global_str;
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_createGlobalRef
(JNIEnv *env, jobject jobj) {
    jobject obj = (*env)->NewStringUTF(env, "JNI is intersting");
    global_str = (*env)->NewGlobalRef(env, obj);//这就创建了一个全局引用
}

JNIEXPORT jstring JNICALL Java_com_mei_test_jni_JniTest_getGlobalRef
(JNIEnv *env, jobject jobj) {
    return global_str;
}

JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_deleteGlobalRef
(JNIEnv *env, jobject jobj) {
    (*env)->DeleteGlobalRef(env, global_str);
}


/*
* 弱全局引用
* 创建方式:NewWeakGlobalRef方法式创建弱全局引用的唯一方式
* 释放方式:自动释放,无需手动释放
* 优点:弱全局引用不会阻止GC,即不会发生内存泄露,不会释放不掉,当内存不足时,优先被释放
*/
jclass g_weak_cls;
JNIEXPORT jstring JNICALL Java_com_mei_test_jni_JniTest_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异常处理
*/
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_exception
(JNIEnv *env, jobject jobj) {
    jclass cls = (*env)->GetObjectClass(env, jobj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");//java中没有字段key,所以会报错

    jthrowable exception = (*env)->ExceptionOccurred(env);
    if (exception !=NULL)//说明发生了异常
    {
        jclass newExc;
        //发送一个异常,让java可以捕捉到
        (*env)->ExceptionClear(env);//清空JNI产生的异常
        //IllegalArgumentException
        newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
        if (newExc ==NULL)
        {
            printf("exception\n");
            return;
        }
        //把异常抛给java层,让java可以捕获
        (*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ");
    }

    printf("exception\n");

}


/*
* JNI 局部静态变量进行缓存
* static 关键字:声明的变量是一个静态变量,static关键字声明的变量,在c语言中,会存储在一个静态区中,
* 存储在静态区中的变量只需要声明和初始化一次,以后再次使用的时候,就可以直接使用,不会再从新创建,
* 使用范围:局部的静态变量只在定义它的方法内有效,其他方法无法引用。
*/
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_cached
(JNIEnv *env, jobject jobj) {
    jclass cls = (*env)->GetObjectClass(env, jobj);
    static jfieldID fid;
    if (fid == NULL) {
        fid = (*env)->GetFieldID(env, cls, "mKey", "Ljava/lang/String;");
        printf("GetFieldID\n");
    }
}


/*
* JNI 全局静态变量进行缓存
* 使用范围:在定义位置之后的所有方法都可以调用,之前的方法无法调用
*/
static jfieldID fid_glb;
JNIEXPORT void JNICALL Java_com_mei_test_jni_JniTest_cachedGlobal
(JNIEnv *env, jobject jobj){
    jclass jclz = (*env)->GetObjectClass(env, jobj);
    if (fid_glb == NULL) {
        fid_glb = (*env)->GetFieldID(env, jclz, "mKey", "Ljava/lang/String;");
        printf("fid_glb init\n");//
    }
}

/*
* JNI 缓存策略和弱引用联合使用带来的问题
*
*/
JNIEXPORT jstring JNICALL Java_com_mei_test_jni_JniTest_accessCacheNewString
(JNIEnv *env, jobject jobj) {
    //定义一个静态的局部变量
    static jclass cls_string = NULL;//当内存不足的时候,在方法调用结束之后,JVM就会释放所有的局部变量,
    //这个时候cls_string这个局部静态变量就会指向一个空的内存空间,即产生了野指针,
    //解决方式:去掉static关键字,如果非要使用static,就一定要做好异常处理
    if (cls_string == NULL)
    {
        printf("alvin in Java_JniMain_AcessCache_newString out: \n");
        //给局部静态变量赋一个局部引用
        cls_string = (*env)->FindClass(env, "com/mei/test/jni/Refrence");
    }
    //使用这个静态局部变量 
    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;
}


/*
* Class:     com_mei_test_jni_JniTest
* Method:    accessCF
* Signature: ()V
*/
JNIEXPORT jstring JNICALL Java_com_mei_test_jni_JniTest_accessCF
(JNIEnv *env, jobject jobj) {
    //Sleep(100);
    return Java_com_mei_test_jni_JniTest_accessCacheNewString(env, jobj);
}

猜你喜欢

转载自blog.csdn.net/u011118524/article/details/77150919