Exception 从异常返回的JNI int方法

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)

假设我有这样一个Java类:

    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端可见,对吗?