以下的代码中展示了如何声明一个抛出异常的Native函数。`CatchThrow`类声明了`doit`这个Native函数,并且指定了它将有可能抛出一个`IllegalArgumentException`.
~~~
public class CatchThrow {
static {
System.loadLibrary("CatchThrow");
}
private native void doit() throws IllegalArgumentException;
private void callback() throws NullPointerException {
throw new NullPointerException("CatchThrow.callback");
}
public static void main(String[] args){
CatchThrow c = new CatchThrow();
try {
c.doit();
}catch (Exception e){
System.out.println("In Java:\n\t" + e);
}
}
}
~~~
CatchThrow.main函数调用的Native方法doit的JNI实现如下:
~~~
/*
* Class: CatchThrow
* Method: doit
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_CatchThrow_doit
(JNIEnv * env, jobject obj){
jthrowable exc;
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V");
if(!mid){
return;
}
(*env)->CallVoidMethod(env, obj, mid);
exc = (*env)->ExceptionOccurred(env);
if(exc){
/*
* We don't do much with the exception, except that we print a debug message for it,
* clear it, and throw a new exception.
*/
jclass newExcCls;
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if(!newExcCls){
/*Unable to find the exception class, give up. */
return;
}
(*env)->ThrowNew(env, newExcCls, "thrown from C code");
}
}
~~~
执行最终的可执行程序将会产生如下输出:
~~~
Exception in thread "main" java.lang.NullPointerException: CatchThrow.callback
at CatchThrow.callback(CatchThrow.java:7)
at CatchThrow.doit(Native Method)
at CatchThrow.main(CatchThrow.java:12)
In Java:
java.lang.IllegalArgumentException: thrown from C code
~~~
回调函数跑出了一个NullPointerException.当CallVoidMethod返回到Native函数时,Native代码通过ExceptionOccurred函数检测到这个异常。在我们的示例中,当异常被检测到的时候, Native代码通过调用ExceptionDescribe输出了一段描述性信息,然后调用ExceptionClear清除了异常信息,最后抛出了一个IllegalArgumentException.
JNI代码引起的异常(例如,通过ThrowNew抛出的异常)不会立即打断Native代码的执行流程。这与Java程序语言中异常处理逻辑是不同的。在Java语言中,如果有异常被抛出,虚拟机就会自动的把控制流程转向匹配异常类型的最近层次的try/catch语句声明,然后虚拟机清空异常并继续执行异常处理逻辑。相比之下,JNI程序员在异常出现的时候必须显示实现代码的控制流程。