有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何捕获JNI/Java异常

我的应用程序中有一个JNI层。在某些情况下,Java抛出异常。如何在JNI层中获得Java异常?我有如下代码

if((*(pConnDA->penv))->ExceptionCheck(pConnDA->penv))
{
    (*(pConnDA->penv))->ExceptionDescribe(pConnDA->penv); 
    (*(pConnDA->penv))->ExceptionClear(pConnDA->penv);
}

这段代码是否只捕获JNI异常?异常描述将记录在控制台(stderr)的何处?如何将其放入缓冲区,以便将其传递给记录器模块


共 (2) 个答案

  1. # 1 楼答案

    这是Elliott Hughes' answer的一个补充。我的回答提供了一个逐步的例子,说明如何捕获异常,以及如何使用JNI层在C++和java字之间转换它们。p>

    简短回答

    请参阅正确的Elliott Hughes' answer

    可重用示例

    为了便于重用,这个答案和代码片段位于公共域或CC0中。这里所有的源代码都是C++03向后兼容的

    要重用上述代码段,请执行以下操作:

    • 用自己的C++异常替换^ {< CD1> }。<李>
    • 如果没有定义相应的Java异常my.group.mypackage.Exception,则将"my/group/mypackage/Exception"替换为"java/lang/RuntimeException"

    捕获Java中的异常

    另见snippet on coliru

    void rethrow_cpp_exception_as_java_exception()
    {
      try
      {
        throw; // This allows to determine the type of the exception
      }
      catch (const mypackage::Exception& e) {
        jclass jc = env->FindClass("my/group/mypackage/Exception");
        if(jc) env->ThrowNew (jc, e.what());
        /* if null => NoClassDefFoundError already thrown */
      }
      catch (const std::bad_alloc& e) {
        jclass jc = env->FindClass("java/lang/OutOfMemoryError");
        if(jc) env->ThrowNew (jc, e.what());
      }
      catch (const std::ios_base::failure& e) {
        jclass jc = env->FindClass("java/io/IOException");
        if(jc) env->ThrowNew (jc, e.what());                          
      }                                                               
      catch (const std::exception& e) {
        /* unknown exception (may derive from std::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, "Unidentified exception => "
          "Improve rethrow_cpp_exception_as_java_exception()" );
      }
    }
    
    <>我感谢Mooing Duck对上面C++代码的贡献。

    调整JNI生成的源代码

    以下文件Java_my_group_mypackage_example.cpp使用上述rethrow_cpp_exception_as_java_exception()函数:

    JNIEXPORT jlong JNICALL Java_my_group_mypackage_example_function1
            (JNIEnv *env, jobject object, jlong value)
    {
      try {
        /* ... my processing ... */
        return jlong(result);
      } catch(...) {
        rethrow_cpp_exception_as_java_exception();
        return 0;
      }
    }
    
    JNIEXPORT jstring JNICALL Java_my_group_mypackage_example_function2
            (JNIEnv *env, jobject object, jlong value)
    {
      jstring jstr = 0
      try {
        /* ... my processing ... */
        jstr = env->NewStringUTF("my result");
      } catch(...) {
        rethrow_cpp_exception_as_java_exception();
      }
      return  jstr;
    }
    
    JNIEXPORT void JNICALL Java_my_group_mypackage_example_function3
            (JNIEnv *env, jobject object, jlong value)
    {
      try {
        /* ... my processing ... */
      } catch(...) {
        rethrow_cpp_exception_as_java_exception();
      }
    }
    

    相应的Java代码

    文件example.java

    package my.group.mypackage;
    
    public class Example {
      static {
        System.loadLibrary("my-DLL-name");
      }
    
      public Example() {
        /* ... */
      }
    
      private native int    function1(int); //declare DLL functions
      private native String function2(int); //using the keyword
      private native void   function3(int); //'native'
    
      public void dosomething(int value) {
        int result = function1(value);  
        String str = function2(value);  //call your DLL functions
        function3(value);               //as any other java function
      }
    }
    

    注:“my-DLL-name”指的是由上述编译的C/C++代码生成的动态库。它可以是Windows上的my-DLL-name.dll,也可以是GNU/Linux/Unix上的my-DLL-name.so

    台阶

    1. example.java生成example.class(使用javac或maven或您最喜欢的IDE Eclipse/Netbeans/IntelliJ IDEA/…)

    2. 使用javah

  2. # 2 楼答案

    如果从JNI调用Java方法,如果Java抛出异常,那么之后调用ExceptionCheck将返回JNI_TRUE

    如果您只是调用一个JNI函数(比如FindClass),ExceptionCheck将告诉您该函数是否失败,并留下一个挂起的异常(正如FindClass在出错时所做的那样)

    ExceptionDescribe输出到stderr。没有方便的方法可以让它去其他地方,但是ExceptionOccurred给了你一个jthrowable如果你想玩它,或者你可以让它去Java并在那里处理。这是通常的风格:

    jclass c = env->FindClass("class/does/not/Exist");
    if (env->ExceptionCheck()) {
      return;
    }
    // otherwise do something with 'c'...
    

    请注意,返回的值并不重要;调用的Java代码永远不会看到它,而是会看到挂起的异常