【问题标题】:How to catch JNI/Java Exception如何捕获 JNI/Java 异常
【发布时间】:2011-01-04 12:07:38
【问题描述】:

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

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

这段代码会只捕获 JNI 异常吗?异常描述会在哪里记录到控制台(stderr)?如何将它放入缓冲区,以便我可以将它传递给我的记录器模块?

【问题讨论】:

标签: java-native-interface


【解决方案1】:

这是对Elliott Hughes' answer 的补充。我的回答提供了一个分步示例,解释了如何捕获异常以及如何使用JNI 层在 C++ 和 Java 单词之间转换它们。

简答

查看正确的Elliott Hughes' answer

可重用示例

此答案和 sn-ps 位于公共领域或 CC0 中,以便于重用。这里所有的源代码都是C++03向后兼容的。

要重用上面的 sn-p,请执行以下操作:

  • 用您自己的 C++ 异常替换 mypackage::Exception
  • 如果未定义对应的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. 使用javahexample.class生成C/C++头文件Java_my_group_mypackage_example.h

【讨论】:

  • 为什么这是一个宏?完全没有必要coliru.stacked-crooked.com/a/5cf4f3b6b1cb988f另外,它没有回答问题
  • 嗨@MooingDuck 感谢您的coliru 链接:-) 我在回答中提供了更多详细信息。请告诉我我的解释是否足以理解宏CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION的用法和自定义。我的目标是提供答案以帮助尽可能多的人。提前感谢您的帮助 ;-) 干杯
  • 我的问题仍然存在。我会尽量说清楚。首先,为什么是宏?宏不好:stackoverflow.com/questions/14041453/…。其次,问题是“如何在 JNI 层中获取 [catch] [当前抛出的] Java 异常?”您的帖子实际上没有回答。 ://
  • 事实上,这种技术可以在没有宏且没有重复的情况下使用。正如我在 coliru 链接中展示的那样,它可以放在一个函数中。检查我的代码中的Java_group_package_class_function1 函数。
  • 这里你不回答问题。
【解决方案2】:

如果您 JNI 调用Java 方法,如果Java 抛出异常,则随后调用ExceptionCheck 将返回JNI_TRUE

如果您只是调用 JNI 函数(例如 FindClass),ExceptionCheck 会告诉您该函数是否以留下未决异常的方式失败(因为 FindClass 会在出错时执行)。

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

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

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

【讨论】:

  • ExceptionDescribe 打印到“系统错误报告通道,例如stderr”,即它取决于实现。
  • 它不起作用。程序在第一行就崩溃了。我尝试了一些尝试/捕获的情况,检查异常但仍然没有运行。有什么解决办法?
  • 必须调用ExceptionClear() if ExceptionCheck() == JNI_TRUE 否则java代码将在控制返回JVM时抛出异常
猜你喜欢
  • 1970-01-01
  • 2013-04-15
  • 2016-03-10
  • 2010-11-25
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多