如何捕获JNI/Java异常

use*_*444 26 java-native-interface

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

if((*(pConnDA->penv))->ExceptionCheck(pConnDA->penv))
{
    (*(pConnDA->penv))->ExceptionDescribe(pConnDA->penv); 
    (*(pConnDA->penv))->ExceptionClear(pConnDA->penv);
}
Run Code Online (Sandbox Code Playgroud)

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

Ell*_*hes 27

如果 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'...
Run Code Online (Sandbox Code Playgroud)

请注意,返回的价值无关紧要; 调用Java代码永远不会看到它 - 它会看到挂起的异常.

  • 如果“ExceptionCheck() == JNI_TRUE”,您*必须*调用“ExceptionClear()”,否则一旦控制权返回到 JVM,java 代码就会抛出异常 (2认同)

oli*_*bre 14

这是Elliott Hughes回答的补充.我的回答提供了一个分步示例,说明如何捕获异常以及如何使用JNI层在C++和Java单词之间进行转换.

简短的回答

看到正确的Elliott Hughes的回答.

可重用的例子

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

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

  • 替换mypackage::Exception为您自己的C++异常.
  • 如果my.group.mypackage.Exception未定义相应的Java异常,则替换"my/group/mypackage/Exception""java/lang/RuntimeException".

从Java捕获异常

另请参阅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()" );
  }
}
Run Code Online (Sandbox Code Playgroud)

我感谢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();
  }
}
Run Code Online (Sandbox Code Playgroud)

对应的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
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:" my-DLL-name"是指从上面编译的C/C++代码生成的动态库.它可以my-DLL-name.dll在Windows上或my-DLL-name.so在GNU/Linux/Unix上.

脚步

  1. 生成example.classexample.java(使用javac或Maven或者您喜欢的IDE的Eclipse/Netbeans的/的IntelliJ IDEA/...)

  2. 生成C/C++头文件Java_my_group_mypackage_example.hexample.class使用javah

  • 在这里你不回答这个问题. (2认同)