从异常返回的 JNI int 方法

Posted

技术标签:

【中文标题】从异常返回的 JNI int 方法【英文标题】:JNI int method returning from exception 【发布时间】:2011-12-03 16:31:14 【问题描述】:

假设我有一个这样的 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 方法中抛出异常,我应该返回什么?

【问题讨论】:

【参考方案1】:

如果您的代码(或库)在 Java 中引发 Exception,那么无论您返回什么值,Java 都会忽略它。显然,它需要是兼容的类型 - 因此在您的示例中返回 0 似乎是有道理的,或者您喜欢的任何东西。当您的代码返回时,Java 运行时会注意到已引发 Exception 并继续传播它并忽略您的函数的返回值。

当然,您需要返回兼容的类型。不要简单地返回NULL,因为当函数未声明返回指针时,这将被强制转换为int,这可能不合适。

但很明显,当您调用 C 函数时,这些函数不会引发 Exception。因此,您可以将整数映射到错误条件(例如 -1),然后在 Java 中抛出 Exception,或者您可以花时间在 JNI 中构建 Exception

【讨论】:

所以你在 Java 中的说法(而不是在 JNI 中)当抛出异常时返回值并不重要。这是真的,因为当抛出异常时,java 方法永远不会到达 return 语句。我在那里和你在一起。但是你是说如果我调用一个“抛出”异常的 JNI 方法并且我没有使用 ExceptionClear 清除它,那么无论我从 JNI 返回什么,返回到 Java 时都会抛出异常?如果是,那么我可以返回任何 jint,并且该 jint 在 Java 端永远不可见,对吧?【参考方案2】:

编辑:   另见this elegant answer using a function instead of the bottom preprocessor macro。


我提供一个例子来完成Edward Thomson's answer。

在此示例中,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

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");            \
  

【讨论】:

以上是关于从异常返回的 JNI int 方法的主要内容,如果未能解决你的问题,请参考以下文章

Android NDK- JNI 异常

Android NDK- JNI 异常

如何使用 JNI 从本机 c 库将 double 和 unsigned int 返回到 java

无法从 JNI 设置 Java int 数组字段

JNI官方中文资料

混合编程jni 第九篇之Jni总结