JNI 学习笔记(三)-- JNI访问数组、引用、异常处理、缓存策略

来源:互联网 发布:win10系统自带优化 编辑:程序博客网 时间:2024/05/22 08:18

1. 访问数组

1. 基本类型的数组

package com.zeking.jni;public class JniTest03 {    public native void getArray(int[] intArray);    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        int [] intArray = {99,66,77,33,22,88};        jniTest03.getArray(intArray);    }}
#include "com_zeking_jni_JniTest03.h"#include <string.h>#include <Windows.h>/** Class:     com_zeking_jni_JniTest03* Method:    getArray* Signature: ([I)V*/// 访问基本类型数据数组JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_getArray(JNIEnv * env, jobject jobj, jintArray jintArray){    int compare(jint *a,jint *b);    jint *elements  = (*env)->GetIntArrayElements(env, jintArray, NULL);    if (elements == NULL){        return;    }    int length = (*env)->GetArrayLength(env, jintArray);    int i = 0;     for (i;i < length; i++){        printf("排序前:第%d个值是:%d\n",i,elements[i]);    }    qsort(elements,length,sizeof(jint),compare);    int j = 0;    for (j; j < length; j++){        printf("排序后:第%d个值是:%d\n", j, elements[j]);    }    (*env)->ReleaseIntArrayElements(env, jintArray, elements, JNI_COMMIT);}int compare(jint *a ,jint *b){    return *a - *b;}

2. 引用类型的数组

package com.zeking.jni;public class JniTest03 {    public native String[] initStringArray(int size);    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        String[] arr = jniTest03.initStringArray(5);        for(int i = 0; i< arr.length;i++){            System.out.println(arr[i]);        }    }}
/** Class:     com_zeking_jni_JniTest03* Method:    initStringArray* Signature: (I)[Ljava/lang/String;*/// 访问引用数据类型的数组JNIEXPORT jobjectArray JNICALL Java_com_zeking_jni_JniTest03_initStringArray(JNIEnv *env, jobject jobj, jint size){    int i;    jclass jclz = (*env)->FindClass(env, "java/lang/String");    if (jclz == NULL){        return NULL;    }    // 创建jobjectArray    jobjectArray 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;    }    // 返回jobjectArray    return result;}

2. JNI引用

1. 局部引用

package com.zeking.jni;public class JniTest03 {    public native void localRef();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        jniTest03.localRef();    }}
/** Class:     com_zeking_jni_JniTest03* Method:    localRef* Signature: ()V*/// JNI 引用// 局部引用// 用来在JNI层生成JVM里面非基本类型数据结构的方法都是可以用于定义局部引用// 定义方式多样:FindClass,NewObject,GetObjectClass,NewCharArray.... NewLocalRef()// 释放方式: 1 方法调用完JVM 会自动释放 2.DeleteLocalRef// 为什么 会自动释放,我们还要手动释放呢?在JNI里面会有一个 JNI局部引用表,// 在ANDROID里面,整个android系统给 这个表 分配的内存他能够存储 比如 512个,// 也就是说在android系统只能在同一时刻最多创建512个局部引用,每创建一个引用// JVM就会把他放到这个JNI局部引用表里面来,如果没有手动及时释放,很可能创建的// 引用一下子就达到了512个,音视频里面容易导致溢出// 局部引用 : 不能在多线程里面使用,只能应用于定义的接口里面JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_localRef(JNIEnv * env, jobject jobj){    int i = 0;    for (i = 0; i < 5; i++){        jclass jclz = (*env)->FindClass(env, "java/util/Date");        // 这个并不是引用,因为他没有定义一个对象,创建一个JVM里面的数据类型对象        jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");        // 创建一个Date类型的局部引用        jobject jobj = (*env)->NewObject(env, jclz, jmid);        // 使用这个引用 ,下面代码省略        // 释放这个引用        (*env)->DeleteLocalRef(env, jclz);        (*env)->DeleteLocalRef(env, jobj);    }}

2. 全局引用

package com.zeking.jni;public class JniTest03 {    public native void createGlobalRef();    public native String getGlobalRef();    public native void delGlobalRef();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        jniTest03.createGlobalRef();        System.out.println(jniTest03.getGlobalRef());        jniTest03.delGlobalRef();        System.out.println("globalRef is released");    }}
// 全局引用// 跨进程,跨方法使用(可以多线程使用)// NewGlobalRef 是创建全局引用的唯一方法jstring global_str;/** Class:     com_zeking_jni_JniTest03* Method:    createGlobalRef* Signature: ()V*/JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_createGlobalRef(JNIEnv * env, jobject jobj){    jobject obj =(*env)->NewStringUTF(env, "JNI is intersting");    global_str =  (*env)->NewGlobalRef(env, obj);}/** Class:     com_zeking_jni_JniTest03* Method:    getGlobalRef* Signature: ()Ljava/lang/String;*/JNIEXPORT jstring JNICALL Java_com_zeking_jni_JniTest03_getGlobalRef(JNIEnv * env, jobject jobj){    return global_str;}/** Class:     com_zeking_jni_JniTest03* Method:    delGlobalRef* Signature: ()V*/JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_delGlobalRef(JNIEnv *env, jobject jobj){    (*env)->DeleteGlobalRef(env, global_str);}//弱全局引用 //它不会阻止GC,/跨线程,跨方法使用//NewWeakGlobalRef 是创建弱全局引用的唯一方法jclass g_weak_cls;JNIEXPORT jstring JNICALL Java_com_zeking_jni_JniTest03_createWeakRef(JNIEnv * env, jobject jobj) {    jclass cls_string = (*env)->FindClass(env, "java/lang/String");    g_weak_cls = (*env)->NewWeakGlobalRef(env, c ls_string);    return g_weak_cls;}

3. 异常

package com.zeking.jni;public class JniTest03 {    public native void exception();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        try {        // 如果不做处理,java是补获不到 exception,后面的java代码没办法继续执行            jniMain.exception();        } catch (Exception e) {            System.out.println(e.toString());        }        System.out.println("----------------异常发生后-------------");         }}
/** Class:     com_zeking_jni_JniTest03* Method:    exception* Signature: ()V*/// 异常展示JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_exception(JNIEnv * env, jobject jobj){    jclass jlaz = (*env)->GetObjectClass(env, jobj);    jfieldID fid = (*env)->GetFieldID(env, jlaz, "key","Ljava/string/String");    printf("exception \n");}

下面是控制台输出的内容
当我们运行的时候会报错,
当我们在找 key 这个id 的时候 ,不存在
但是 printf(“exception \n”); 还是打印出来了,如果是java的话,如果报了异常,我们不去捕获他,后面的代码没办法继续执行

Exception in thread "main" java.lang.NoSuchFieldError: key    at com.zeking.jni.JniTest03.exception(Native Method)    at com.zeking.jni.JniTest03.main(JniTest03.java:40)exception 

如何让java层顺利的执行后面的代码呢?
如何让java层顺利的捕获到jni的异常呢?

/** Class:     com_zeking_jni_JniTest03* Method:    exception* Signature: ()V*/// 抛出异常JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_exception(JNIEnv * env, jobject jobj){    jclass cls = (*env)->GetObjectClass(env, jobj);    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");    //检查是否发送异常,java 里面的检查是 try catch    jthrowable ex = (*env)->ExceptionOccurred(env);    // 判断异常是否发送 ,相当于 catch ,说明发生了异常    if (ex != NULL) {        jclass newExc;        //清空JNI 产生的异常,如果只是这句代码,没有后面的,这样清空了代码,java层可以继续执行后面的代码        //即可以打印出----------------异常发生后------------- 这个字符串        (*env)->ExceptionClear(env);        // 模拟 java中的  IllegalArgumentException        newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");        if (newExc == NULL)        {            printf("exception newExc NULL \n");            return;        }        (*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ");    }}

结果打印

java.lang.IllegalArgumentException: The exception from JNI:GetFieldID failed-----------发生异常后 ----------exception 

后面会写一篇关于 异常的工具类的 文章,还有 打印log的工具类的文章

4. 缓存

1. 局部静态变量进行缓存

package com.zeking.jni;public class JniTest03 {    public String key = "zeking";    public native void cached();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        for (int i = 0; i < 5; i++) {            jniTest03.cached();        }    }}
/** Class:     com_zeking_jni_JniTest03* Method:    cached* Signature: ()V*/// 局部静态变量进行缓存.JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_cached(JNIEnv * env, jobject jobj){    jclass jclz = (*env)->GetObjectClass(env, jobj);    // static 关键字就是告诉我们他是一个静态变量,如果定义的静态变量是一个局部变量    // 这个局部变量会存储在 static的 静态存储区,    // 意味着他只要定义初始化一次,以后在访问同一个静态区的时候,    // 他会一直有定义,不会被释放    static jfieldID fid = NULL;    if (fid == NULL){        fid = (*env)->GetFieldID(env, jclz,"key", "Ljava/lang/String;");        printf("GetFieldId \n");    }}

2. 全局变量进行缓存

package com.zeking.jni;public class JniTest03 {    public String key = "zeking";    public native static void cachedGlobal();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        for (int i = 0; i < 5; i++) {            cachedGlobal();        }    }}
static jfieldID global_fid;// static 全局只能在这个定义的后面才能被使用,/** Class:     com_zeking_jni_JniTest03* Method:    cachedGlobal* Signature: ()V*/// 全局变量JNIEXPORT void JNICALL Java_com_zeking_jni_JniTest03_cachedGlobal(JNIEnv * env, jclass jclz){    if (global_fid == NULL){        global_fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");        printf("GetFieldId Global\n");    }}

5. 缓存策略和弱引用联合使用带来的问题

package com.zeking.jni;public class Refence {    public int getRef(int num) {        return num;    }}
package com.zeking.jni;public class JniTest03 {    public native String acessCacheNewString();    public native String acessCF();    static{        System.loadLibrary("JniTest03");    }    public static void main(String[] args) {        JniTest03 jniTest03 = new JniTest03();        for(int i = 0 ; i < 10 ; i++){            jniTest03.acessCF();            long[] arr = new long[1024*1024];        }    }}
/** Class:     com_zeking_jni_JniTest03* Method:    acessCacheNewString* Signature: ()Ljava/lang/String;*/// 缓存策略和弱引用联合使用带来的问题JNIEXPORT jstring JNICALL Java_com_zeking_jni_JniTest03_acessCacheNewString(JNIEnv * env , jobject jobj){    // 定义一个静态的局部变量    static jclass cls_string = NULL;    if (cls_string == NULL){        printf("in   Java_com_zeking_jni_JniTest03_acessCacheNewString \n");         // 给局部静态变量赋一个局部引用        cls_string = (*env)->FindClass(env,"com/zeking/jni/Refence" );    }    // 使用这个静态局部变量 ,, 第二次进来,这边可能出现了问题,因为这个cls_string 可能指向一个被JVM释放,指向一个野指针。    jmethodID jmi = (*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 exception happend\n");    }    printf("out   Java_com_zeking_jni_JniTest03_acessCacheNewString \n");    return NULL;}/** Class:     com_zeking_jni_JniTest03* Method:    acessCF* Signature: ()Ljava/lang/String;*/// 调用 acessCF 方法 其实他里面就是调用了acessCacheNewString方法,// 当调用完 acessCF 方法之后,他里面的东西就被释放了,但是// acessCacheNewString 中的cls_string 是一个  静态的 局部变量,他没有被释放// 当我们再次调用 AcessCF 方法之后,cls_string 指向的是一个被释放的区域(指向一个野指针)JNIEXPORT jstring JNICALL Java_com_zeking_jni_JniTest03_acessCF(JNIEnv * env, jobject jobj){    return Java_com_zeking_jni_JniTest03_acessCacheNewString(env, jobj);}

下面就是控制台输出的内容 ,发生了异常

java.lang.NoSuchMethodError: getRef    at com.zeking.jni.JniTest03.acessCF(Native Method)    at com.zeking.jni.JniTest03.main(JniTest03.java:67)java.lang.NoSuchMethodError: getRef    at com.zeking.jni.JniTest03.acessCF(Native Method)    at com.zeking.jni.JniTest03.main(JniTest03.java:67)in   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString out   Java_com_zeking_jni_JniTest03_acessCacheNewString c exception happendout   Java_com_zeking_jni_JniTest03_acessCacheNewString c exception happendout   Java_com_zeking_jni_JniTest03_acessCacheNewString Exception in thread "main" Exception in thread "main" 
原创粉丝点击