Exception 从异常返回的JNI int方法
假设我有这样一个Java类:Exception 从异常返回的JNI int方法,exception,java-native-interface,android-ndk,jint,Exception,Java Native Interface,Android Ndk,Jint,假设我有这样一个Java类: public class Test { static { System.loadLibrary("test"); } public native int foo(); } 假设foo()方法正在执行一些JNI调用,其中一个调用失败(即抛出异常)。然后我想从JNI代码返回,并在Java中抛出异常。例如: jint Java_Test_foo(JNIEnv* env, jobject thiz)
public class Test
{
static { System.loadLibrary("test"); }
public native int foo();
}
假设foo()方法正在执行一些JNI调用,其中一个调用失败(即抛出异常)。然后我想从JNI代码返回,并在Java中抛出异常。例如:
jint Java_Test_foo(JNIEnv* env, jobject thiz)
{
jstring foobar = (*env)->NewStringUTF(env, "Hello from JNI !");
if(foobar == NULL) // Not enough memory. OutOfMemoryError is thrown
return NULL; // Return immediately to get exception thrown in Java
// Do other stuff
...
return some_computed_jint;
}
问题在于returnnull
不是一件坏事。例如,在Android中,我在编译时会收到以下警告:
警告:return从指针生成整数,不带强制转换
现在的问题是:如果在返回jint的JNI方法中抛出异常,我应该返回什么?如果您的代码(或库)在Java中引发了异常,那么无论返回什么值,Java都会忽略它。显然,它需要是一个兼容的类型-因此在您的示例中返回0
似乎是有意义的,或者是您喜欢的任何类型。当代码返回时,Java运行时将注意到引发了一个异常
,并继续传播它,忽略函数的返回值
当然,您需要返回一个兼容的类型。不要简单地返回NULL
,因为当函数未声明返回指针时,这将转换为int
,这可能不合适
显然,当您调用C函数时,这些函数不会引发异常。因此,您可以将一个整数映射到一个错误条件(例如,-1
),然后在Java中抛出异常
,或者您可以花时间在JNI中构建一个异常
。如果您的代码(或库)在Java中引发了一个异常
,无论返回什么值,Java都会忽略它。显然,它需要是一个兼容的类型-因此在您的示例中返回0
似乎是有意义的,或者是您喜欢的任何类型。当代码返回时,Java运行时将注意到引发了一个异常
,并继续传播它,忽略函数的返回值
当然,您需要返回一个兼容的类型。不要简单地返回NULL
,因为当函数未声明返回指针时,这将转换为int
,这可能不合适
显然,当您调用C函数时,这些函数不会引发异常。因此,您可以将一个整数映射到一个错误条件(例如,-1
),然后在Java中抛出异常
,也可以花时间在JNI中构建异常。编辑: 另见
我提供了一个例子来完成这个过程
在本例中,JNI非void函数返回0代码>
JNIEXPORT jlong JNICALL Java_group_package_class_function1(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
return jlong(result);
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
return 0;
}
JNIEXPORT jstring JNICALL Java_group_package_class_function2(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
jstring jstr = env->NewStringUTF("my result");
return jstr;
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
return 0;
}
JNIEXPORT void JNICALL Java_group_package_class_function3(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
} // void function => no "return 0;" statement
上述所有JNI函数的末尾都有C预处理器宏CATCH_CPP_EXCEPTION_和THROW_JAVA_EXCEPTION
#define CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION \
\
catch (const package::Exception& e) \
{ \
jclass jc = env->FindClass("group/package/Exception"); \
if(jc) env->ThrowNew (jc, e.what()); \
/* if null => NoClassDefFoundError already thrown */ \
} \
catch (const std::bad_alloc& e) \
{ \
/* OOM exception */ \
jclass jc = env->FindClass("java/lang/OutOfMemoryError"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (const std::ios_base::failure& e) \
{ \
/* IO exception */ \
jclass jc = env->FindClass("java/io/IOException"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (const std::exception& e) \
{ \
/* unknown exception */ \
jclass jc = env->FindClass("java/lang/Error"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (...) \
{ \
/* Oops I missed identifying this exception! */ \
jclass jc = env->FindClass("java/lang/Error"); \
if(jc) env->ThrowNew (jc, "unexpected exception"); \
}
编辑: 另见
我提供了一个例子来完成这个过程
在本例中,JNI非void函数return 0代码>
JNIEXPORT jlong JNICALL Java_group_package_class_function1(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
return jlong(result);
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
return 0;
}
JNIEXPORT jstring JNICALL Java_group_package_class_function2(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
jstring jstr = env->NewStringUTF("my result");
return jstr;
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
return 0;
}
JNIEXPORT void JNICALL Java_group_package_class_function3(
JNIEnv *env, jobject object, jlong value)
{
try
{
/* ... my processing ... */
}
CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
} // void function => no "return 0;" statement
上述所有JNI函数的末尾都有C预处理器宏CATCH_CPP_EXCEPTION_和THROW_JAVA_EXCEPTION
#define CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION \
\
catch (const package::Exception& e) \
{ \
jclass jc = env->FindClass("group/package/Exception"); \
if(jc) env->ThrowNew (jc, e.what()); \
/* if null => NoClassDefFoundError already thrown */ \
} \
catch (const std::bad_alloc& e) \
{ \
/* OOM exception */ \
jclass jc = env->FindClass("java/lang/OutOfMemoryError"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (const std::ios_base::failure& e) \
{ \
/* IO exception */ \
jclass jc = env->FindClass("java/io/IOException"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (const std::exception& e) \
{ \
/* unknown exception */ \
jclass jc = env->FindClass("java/lang/Error"); \
if(jc) env->ThrowNew (jc, e.what()); \
} \
catch (...) \
{ \
/* Oops I missed identifying this exception! */ \
jclass jc = env->FindClass("java/lang/Error"); \
if(jc) env->ThrowNew (jc, "unexpected exception"); \
}
因此,您在Java中(而不是在JNI中)所说的返回值在抛出异常时并不重要。这是正确的,因为当抛出异常时,java方法永远不会到达return语句。我支持你。但是你是说,如果我调用一个“抛出”异常的JNI方法,而我没有使用ExceptionClear清除它,那么无论我从JNI返回什么,当返回到Java时都会抛出异常?如果是,那么我可以返回任何jint,并且jint在Java端永远不会可见,对吗?因此您在Java中说(而不是在JNI中说)返回值在抛出异常时并不重要。这是正确的,因为当抛出异常时,java方法永远不会到达return语句。我支持你。但是你是说,如果我调用一个“抛出”异常的JNI方法,而我没有使用ExceptionClear清除它,那么无论我从JNI返回什么,当返回到Java时都会抛出异常?如果是,那么我可以返回任何jint,并且该jint将永远不会在Java端可见,对吗?