JNI 学习笔记(三)-- JNI访问数组、引用、异常处理、缓存策略
2017-10-09 22:40
816 查看
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 happend out Java_com_zeking_jni_JniTest03_acessCacheNewString c exception happend out Java_com_zeking_jni_JniTest03_acessCacheNewString Exception in thread "main" Exception in thread "main"
相关文章推荐
- NDK开发学习笔记(3):JNI访问数组、引用、异常处理、缓存策略
- JNI学习笔记5——本地方法处理java数组/引用问题/缓存jfieldID/jmethodID
- NDK-JNI语法-数组处理+全局引用+异常处理+缓存策略
- JNI学习笔记:(1)开篇(2)本地代码访问Java代码 (3)本地方法取得Java属性/调用java方法 (4)本地代码创建Java对象(包括javaString) (5) 本地方法处理java数组
- JNI学习笔记:异常处理
- Effective C# 学习笔记(四十七)对异常进行strong guarantee 策略处理
- Android NDK (学习笔记六) —— JNI交互间数组的处理
- Effective C# 学习笔记(三十七) 警惕并行处理中的异常处理
- [转载]关于SQLServer2005的学习笔记——异常捕获及处理
- PL/SQL学习笔记-异常处理
- oralce学习笔记之异常处理篇
- Effective C# 学习笔记(四十六)对异常进行分类并逐类处理
- 异常处理程序学习笔记
- 精通SqlServer2005学习笔记----Sqlserver事物中的异常处理
- 关于SQLServer2005的学习笔记——异常捕获及处理
- 20101109 学习记录: C#.net访问web URL并处理返回值 && 不加web引用调用webservice
- j2me学习笔记【5】——抛出异常处理的小例子
- va异常处理学习笔记
- arm体系结构学习笔记 part4 -- 异常处理的返回
- C/C++中关于地址、指针和引用变量的学习笔记(二) : 数组