Как избежать UnsatisfiedLinkError при вызове C ++ из Java из приложения C ++? - PullRequest
6 голосов
/ 01 августа 2011

Я встраиваю Java в приложение C ++.В рамках этого мне нужно представить нативные функции для java, а также вызвать функции java из C ++.

Нужно ли помещать функции, которые я хочу вызвать из 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

}

Ответы [ 4 ]

5 голосов
/ 25 августа 2011

Обычно JVM ожидает найти собственные определения методов в общей библиотеке, которая была загружена с помощью System#load или System#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));
2 голосов
/ 01 августа 2011

Попробуйте это: При запуске приложения Java добавьте отсутствующий файл ссылки с помощью «LD_LIBRARY_PATH»

Что-то вроде

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

Путь может использовать абсолютный путь. Надеюсь, что это может быть полезно.

2 голосов
/ 01 августа 2011

Прошло много времени с тех пор, как я связался с JNI, поэтому я немного увлечен этой темой.Я думаю, что ваша проблема в том, что вы объявляете метод get_property как native.Это означает, что JVM ожидает найти общую библиотеку, предоставляющую метод get_property.Вот документация по java.lang.UnsatisfiedLinkError.

UnsatisfiedLinkError выбрасывается, когда (1) пытается вызвать собственный метод, который не был загружен, или (2), когда loadLibrary илиМетод загрузки в среде выполнения или System вызывается для файла, который не может быть найден.

Вы объявляете метод Java native только если вы собираетесь реализовать этот метод в C или C ++, а затем вызываетеэто с Java.Поскольку вы пытаетесь сделать обратное, то есть вызывать методы Java из собственного кода, вам необходимо реализовать метод get_property в Java.В нативном коде вы затем создадите экземпляр класса TestObject и вызовете метод get_property для этого экземпляра.

Я нашел учебник Sun о том, как встроить JVM внативный код книга сама начинается с примеров того, как вызывать нативный код из Java.

1 голос
/ 01 августа 2011

Я думаю, вы должны попробовать написать функцию 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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...