假设我有一个像这样的Java类:
public class Test
{
static { System.loadLibrary("test"); }
public native int foo();
}
假设foo()方法正在执行一些JNI调用,并且其中一个调用失败(IE,抛出异常)。然后我想从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;
}
问题是return NULL
不是jint。例如,在Android中,我会在编译时收到此警告:
警告:return从指针生成整数而不使用强制转换。
现在问题是:如果在返回jint的JNI方法中抛出异常,我应该返回什么?
答案 0 :(得分:4)
如果您的代码(或库)在Java中引发Exception
,那么返回的值无关紧要,Java会忽略它。显然它需要是一个兼容的类型 - 所以在你的例子中返回0
似乎是有意义的,或者你喜欢的任何东西。当您的代码返回时,Java运行时将注意到已经引发Exception
并继续传播它并忽略函数的返回值。
当然,您需要返回兼容类型。不要简单地返回NULL
,因为当未声明函数返回指针时,它将被转换为int
,这可能不合适。
显然,当你调用C函数时,那些不会引发Exception
。因此,您可以将整数映射到错误条件(例如-1
),然后在Java中抛出Exception
,或者您可以花时间在JNI中构建Exception
。 / p>
答案 1 :(得分:0)
编辑:另请参阅this elegant answer using a function instead of the bottom preprocessor macro。
我提供了一个完成Edward Thomson's answer的示例。
在此示例中,JNI非空函数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
C预处理器宏CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
出现在所有上述JNI函数的末尾。
#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"); \
}