野指针
野指针指向的是一个无效的地址。
Crash
,内核给进程发送错误的信号SIGSEGV
,这时候bug会很快被发现。NULL
(如果在别处,也有指针指向该处的内存这种方式就不好解决)。try...catch
或者throws
进行处理,否则无法编译通过,比如File,stream
,线程操作等;RuntimeException
及其子类都统称为非受检查异常,例如:NullPointExecrption、NumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)
等;try...catch
机制来捕获并处理异常;try...catch
来捕获,会导致程序直接奔溃退出,后续的代码都不会被执行;try...catch...final
这样的异常处理机制,面且在本地代码中调用某个JNI接口时如果发生了异常,后续的本地代码不会立即停止执行,而会继续往下执行后面的代码;ExceptionCheck
ExceptionOccurred
调用了JNI的ExceptionCheck
函数检查最近一次JNI调用是否发生了异常,如果有异常这个函数返回JNI_TRUE
,否则返回JNI_FALSE
//异常捕获 ,检查JNI调用是否有异常 if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear();//清除引发的异常,在Java层不会打印异常堆栈信息,如果不清除,后面的调用ThrowNew抛出的异常堆栈信息会 //覆盖前面的异常信息 jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return; }
123456789 | //异常捕获 ,检查JNI调用是否有异常if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear();//清除引发的异常,在Java层不会打印异常堆栈信息,如果不清除,后面的调用ThrowNew抛出的异常堆栈信息会//覆盖前面的异常信息 jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return;} |
---|
异常检查JNI还提供了另外一个接口,ExceptionOccurred
,如果检测有异常发生时,该函数会返回一个指向当前异常的引用。作用和ExceptionCheck
一样,两者的区别在于返回值不一样。
//异常捕获,第二种方法 if(env->ExceptionOccurred()){ env->ExceptionDescribe(); env->ExceptionClear(); jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return; }
12345678 | //异常捕获,第二种方法if(env->ExceptionOccurred()){ env->ExceptionDescribe(); env->ExceptionClear(); jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return;} |
---|
void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { // 查找异常类 jclass cls = (*env)->FindClass(env, name); /* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */ if (cls != NULL) { (*env)->ThrowNew(env, cls, msg); // 抛出指定名字的异常 } /* 释放局部引用 */ (*env)->DeleteLocalRef(env, cls); }
1234567891011 | void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { // 查找异常类 jclass cls = (*env)->FindClass(env, name); /* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */ if (cls != NULL) { (*env)->ThrowNew(env, cls, msg); // 抛出指定名字的异常 } /* 释放局部引用 */ (*env)->DeleteLocalRef(env, cls); } |
---|
在异常发生后,释放资源是一件很重要的事情。下面的例子中,调用 GetStringChars
函数后,如果后面的代码发生异常,要记得调用 ReleaseStringChars
释放资源。
JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr) { const jchar *cstr = (*env)->GetStringChars(env, jstr); if (c_str == NULL) { return; } ... if ((*env)->ExceptionCheck(env)) { /* 异常检查 */ (*env)->ReleaseStringChars(env, jstr, cstr); // 发生异常后释放前面所分配的内存 return; } ... /* 正常返回 */ (*env)->ReleaseStringChars(env, jstr, cstr); }
123456789101112131415 | JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr) { const jchar *cstr = (*env)->GetStringChars(env, jstr); if (c_str == NULL) { return; } ... if ((*env)->ExceptionCheck(env)) { /* 异常检查 */ (*env)->ReleaseStringChars(env, jstr, cstr); // 发生异常后释放前面所分配的内存 return; } ... /* 正常返回 */ (*env)->ReleaseStringChars(env, jstr, cstr);} |
---|
ExceptionClear
清除异常,然后执行自己的异常处理代码;ExceptionCheck
:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE;ExceptionOccurred
:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL;ExceptionDescribe
:打印异常的堆栈信息;ExceptionClear
:清除异常堆栈信息;ThrowNew
:在当前线程触发一个异常,并自定义输出异常信息
jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
Throw
:丢弃一个现有的异常对象,在当前线程触发一个新的异常
jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj);
FatalError
:致命异常,用于输出一个异常信息,并终止当前JVM实例(即退出程序;
void (JNICALL *FatalError) (JNIEnv *env, const char *msg);