JNI基础-缓存策略和异常处理

数组处理的一些细节

首先我们写java代码交给c语言对数组进行排序

   public static void main(String[] args) {
      //数组处理的一些细节
        int[] arr={11,22,3,-5,4,9,-15};
        sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]+"\t");
        }
    }

生成头文件,并将头文件和jni.h,jni_md.h两个文件拷贝到新建的vs项目中的相关目录下
c语言实现数组排序代码

#include "com_example_demo_test.h"
#include<stdio.h>
#include<stdlib.h>

int compare(const int *number1, const int*number2){

    return *number1 - *number2;
}
JNIEXPORT void JNICALL Java_com_example_demo_test_sort
(JNIEnv *env, jclass clazz, jintArray jarray){
    //对jarray进行排序
    //第一个参数:void* 数组的首地址
    jint* intArray=(*env)->GetIntArrayElements(env, jarray, NULL);
    //第二个参数 num 是 参与排序的目标数组元素个数
    int length = (*env)->GetArrayLength(env, jarray);
    //第三个参数 数组元素数据类型的大小
    //第四个参数,数组的一个方法比较指针
    qsort(intArray, length, sizeof(int), compare);
    //同步数组的数据给java数组,intArray并不是jarray,可以理解为简单的copy
    //0:既要同步数据给jarray,又要释放intArray
    //JNI_COMMIT:会同步数据给jArray,且会释放intArray
    //JNI_ABORT:不同步数据给jArray,但是会释放intArray
    (*env)->ReleaseIntArrayElements(env, jarray, intArray, JNI_ABORT);
}

生成相应的dll库,具体方法大家可以参考我这篇:JNI 基础 – JNIEnv 的实现原理https://www.jianshu.com/p/d4a502420a89
java代码中引入动态库

   static {
        System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
    }
}

局部引用和全局引用

局部引用,java代码

 private static native void localRef();

c代码

JNIEXPORT void JNICALL Java_com_example_demo_test_localRef
(JNIEnv *env, jclass jclz){
    //字符串截取,String对象
    jclass str_clz = (*env)->FindClass(env, "java/lang/String");
    //构造函数
    jmethodID init_mid = (*env)->GetMethodID(env, str_clz, "<init>", "()V");
    jobject j_str = (*env)->NewObject(env, str_clz, init_mid);
    //jobject不要再使用了,要回收
    (*env)->DeleteLocalRef(env, j_str);
    //删除后就不能再使用,c和c++都需要自己去释放内存(静态开辟不需要,动态开辟的需要)
}

全局引用java代码

 //局部引用和全局引用
       public static void main(String[] args) {
        //局部引用和全局引用
        //localRef();
        savelobalRef("Peakmain");
        System.out.println(getGlobalRef());
        //合适的时机去释放
        deleteGlobalRef();
        //再次获取,此时会报空指针异常错误
        System.out.println(getGlobalRef());
    }
   private static native void savelobalRef(String name);
    private static native String getGlobalRef();
    private static native void deleteGlobalRef();

c代码的实现

//全局变量
jstring globalStr;
//保存全局变量
JNIEXPORT void JNICALL Java_com_example_demo_test_savelobalRef
(JNIEnv *env, jclass jcls, jstring str) {
    //保存全局变量,其他方法需要到
    globalStr = (*env)->NewGlobalRef(env, str);

}
//获得全局变量
JNIEXPORT jstring JNICALL Java_com_example_demo_test_getGlobalRef
(JNIEnv *env, jclass jclz){
    return globalStr;
}
//删除全局变量
JNIEXPORT jstring JNICALL Java_com_example_demo_test_deleteGlobalRef
(JNIEnv *env, jclass jclz) {
    (*env)->DeleteGlobalRef(env, globalStr);
}

静态缓存策略

局部缓存策略
java代码

   private static String name;
    public static void main(String[] args) {
        //3.缓存策略 static,native中有一大堆方法要去获取name属性
        staticLocalCache("Peakmain");
        System.out.println("name="+name);
        staticLocalCache("Treasure");
        System.out.println("name="+name);
        staticLocalCache("Body");
        System.out.println("name="+name);
    }
 private static native void staticLocalCache(String name);

c代码的实现

JNIEXPORT void JNICALL Java_com_example_demo_test_staticLocalCache
(JNIEnv *env, jclass jclz, jstring name) {
    //name属性赋值
    static jfieldID j_fid = NULL;//加了一个局部缓存,如果这个方法会被多次调用,我们不需要反复去获取jfieldID
    if (j_fid == NULL) {
        j_fid = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
    }
    else {
        printf("field is not NULL\n");
    }
    (*env)->SetStaticObjectField(env, jclz, j_fid, name);
}

全局缓存策略
java代码

        //初始化全局静态缓存
       private static native void initStaticCache();

c代码的实现

//全局静态缓存,在构造函数初始化的时候
static jfieldID j_fid1 = NULL;
static jfieldID j_fid2 = NULL;
static jfieldID j_fid3 = NULL;
JNIEXPORT jstring JNICALL Java_com_example_demo_test_initStaticCache
        (JNIEnv *env, jclass jclz){
    //初始化全局静态缓存
    j_fid1 = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
    j_fid2 = (*env)->GetStaticFieldID(env, jclz, "name1", "Ljava/lang/String;");
    j_fid3 = (*env)->GetStaticFieldID(env, jclz, "name2", "Ljava/lang/String;");
}
JNIEXPORT void JNICALL Java_com_example_demo_test_staticLocalCache
        (JNIEnv *env, jclass jclz, jstring name) {
    //如果这个方法反复被调用,那么不会反复获取name属性
    (*env)->SetStaticObjectField(env, jclz, j_fid1, name);
    (*env)->SetStaticObjectField(env, jclz, j_fid2, name);
    (*env)->SetStaticObjectField(env, jclz, j_fid3, name);
}

jni异常处理

java代码

public static void main(String[] args) {
      //异常处理
        try {
            exception();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        System.out.println("name = " + name3);
    }
    private static native void exception() throws NoSuchFieldException;

c代码的实现

JNIEXPORT void JNICALL Java_com_example_demo_test_exception
        (JNIEnv *env, jclass jclz) {
    //假设现在想给name进行赋值,但是现在拿没有的name3
    jfieldID j_fid= (*env)->GetStaticFieldID(env, jclz, "name3", "Ljava/lang/String;");
    jstring name=(*env)->NewStringUTF(env,"Peakmain");//这时候会抛异常
    //解决办法
    //1.补救措施,name3没有拿name
    //1.1判断有没有异常
    jthrowable throwable=(*env)->ExceptionOccurred(env);
  /*  if(throwable){
        //补救措施
        printf("有异常");
        //先把异常清除一下
        (*env)->ExceptionClear(env);
        //重新获取name属性
        j_fid= (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
    }*/
    //2.给java层抛一个异常
    if(throwable){
        //补救措施
        printf("有异常");
        //先把异常清除一下
        (*env)->ExceptionClear(env);
       //抛出一个java异常Throwable对象
        jclass no_such_clz=(*env)->FindClass(env,"java/lang/NoSuchFieldException");
        (*env)->ThrowNew(env, no_such_clz,"NoSuchFieldException name3");
        return ;//记得return
    }
    (*env)->SetStaticObjectField(env, jclz, j_fid, name);
}

native出错了没办法在java层try,处理方式一般两种,一个是补救,一个是构建java异常对象向上抛

    原文作者:Peakmain
    原文地址: https://www.jianshu.com/p/66b8daf8f98b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞