如何从c++应用程序从java调用c++时避免UnsatisfiedLinkError ?

How do I avoid UnsatisfiedLinkError when calling C++ from java from C++ application?

本文关键字:c++ UnsatisfiedLinkError java 应用程序 调用      更新时间:2023-10-16

我正在将Java嵌入到c++应用程序中。作为其中的一部分,我需要向java公开本地函数,以及从c++调用java函数。

我需要把我想从java调用的函数到一个共享库吗?或者它们可以以某种方式编译到宿主应用程序中?

这是我到目前为止所尝试的,但是它给出了java.lang.UnsatisfiedLinkError

编译

我在OS X 10.5上使用

构建
g++ -Wall -I/System/Library/Frameworks/JavaVM.framework/Headers/ -framework JavaVM -g test.cpp

Java测试文件:TestObject.java

// To build this you need to do a `javac TestObject.java`
// To get the signatures do a `javap -d TestObject`
// To generate the .h file do a `javah TestObject`
public class TestObject
{
    public native TestObject get_property( String k ); 
}
c++测试文件:Test .cpp
#include <jni.h>
#include <assert.h>

JNIEXPORT jobject JNICALL Java_TestObject_get_1property(JNIEnv * jni_env, jobject obj, jstring key)
{
  //Just a stub implementation for now.
  jclass klass = jni_env->GetObjectClass( obj );
  jmethodID constructor = jni_env->GetMethodID( klass, "<init>", "()V");
  jobject retval = jni_env->NewObject(klass, constructor );
  return retval;
}


int main()
{
  JavaVM* jvm;
  JavaVMInitArgs vm_args;
  JavaVMOption options[1];
  vm_args.version = JNI_VERSION_1_4;
  vm_args.nOptions = 1;
  options[0].optionString = "-Djava.class.path=.";
  vm_args.options = options;
  vm_args.ignoreUnrecognized = JNI_FALSE;
  JNIEnv * env;
  JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
  jclass klass = (env)->FindClass("TestObject");
  assert( klass );
  jmethodID constructor = env->GetMethodID( klass, "<init>", "()V");
  assert( constructor );
  jobject obj = env->NewObject(klass, constructor );
  jmethodID test_method = (env)->GetMethodID( klass, "get_property", "(Ljava/lang/String;)LTestObject;" );
  assert( test_method );
  jvalue args[1];
  args[0].l = env->NewStringUTF("k");
  jobject rv = env->CallObjectMethodA(obj, test_method, args );
  jthrowable exc = env->ExceptionOccurred();
  if(exc)
  {
    env->ExceptionDescribe();
    env->ExceptionClear();
  }
  //TODO: do something with rv

}

通常情况下,JVM期望在通过System#loadSystem#loadLibrary加载的共享库中找到本机方法定义,在大多数情况下,这是最方便的方法。然而,对于像您这样的情况,确实存在另一种选择,您希望在可执行文件中直接包含实现。

如果调用JNIEnv::RegisterNatives,则可以向JVM传递一个函数指针列表,该列表对应于特定类中的本机方法。当一些Java代码调用其中一个方法时,JVM将知道调用传递给RegisterNatives的函数指针,而不是在动态加载的库中搜索。

JNINativeMethod methods[] = {
    {
        "frobFabulously",
        "(Ljava/lang/Object;)V",
        reinterpret_cast<void*>(NativeFrobFabulouslyImpl)
    },
};
env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod));

我使用JNI已经有一段时间了,所以我对这个话题有点生疏了。我认为你的问题是你将get_property方法声明为native。这意味着JVM期望找到一个公开get_property方法的共享库。以下是java.lang.UnsatisfiedLinkError的文档。

UnsatisfiedLinkError在(1)尝试调用本机时抛出(2)当loadLibrary或load方法时在运行时或系统中为找不到的文件调用。

你声明一个Java方法native只有当你打算在C或c++中实现该方法,然后从Java调用它。因为你试图做相反的事情,即从本机代码调用Java方法,你需要在Java中实际实现get_property方法。在本机代码中,您将创建TestObject的类实例,并在该实例上调用get_property方法。

我找到了一个关于如何在本机代码中嵌入JVM的Sun教程。本书从如何从Java调用本机代码的示例开始。

试试这个:当您执行Java应用程序时,使用"LD_LIBRARY_PATH"添加缺失的链接文件

之类的
LD_LIBRARY_PATH=[the link file path need be included] java xxx.class

路径可以使用绝对路径。

我认为您应该尝试在另一个文件中编写JNI函数。当您使用javah TestObject.java时,将生成一个文件TestObject.h。用实现的函数创建一个文件TestObject.c。然后使用本机代码构建一个共享库。(类似于g++ -G -I/pkgs/jdk1.4/include TestObject.C -o libTestObject.so)同样在TestObject.java中,像static{ System.loadLibrary("TestIbject");一样静态加载库libTestObject。so应该添加到LD_LIBRARY_PATH(在Linux环境中)