I'm embedding Java into a C++ application. As part of this I need to expose native functions to java, as well as calling java functions from C++.
Do I need to put the functions I want to call from java into a shared library? Or can they be compiled into the host application somehow?
Here's what I've tried so far, but it gives a java.lang.UnsatisfiedLinkError
Compilation
I'm building on OS X 10.5 using
g++ -Wall -I/System/Library/Frameworks/JavaVM.framework/Headers/ -framework JavaVM -g test.cpp
Java Test File : 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 File : 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 optio开发者_如何学Cns[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
}
Normally the JVM expects to find native method definitions in a shared library that has been loaded via System#load
or System#loadLibrary
, and in most cases that is the most convenient approach. However, there does exist an alternative for situations like yours, where you would prefer to include the implementations directly in your executable.
If you call JNIEnv::RegisterNatives
, you can instead pass the JVM a list of function pointers corresponding to the native methods in a particular class. When some Java code calls one of those methods, the JVM will know to invoke the function pointer you passed to RegisterNatives
instead of searching through dynamically-loaded libraries.
JNINativeMethod methods[] = {
{
"frobFabulously",
"(Ljava/lang/Object;)V",
reinterpret_cast<void*>(NativeFrobFabulouslyImpl)
},
};
env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod));
It's been a while since I've messed with JNI, so I'm a little rusty on the topic. I think your problem is that you're declaring the get_property
method as native
. This means that the JVM expects to find a shared library exposing the get_property
method. Here's the documentation on java.lang.UnsatisfiedLinkError
.
UnsatisfiedLinkError
is thrown when (1) attempting to call a native method that has not been loaded or (2) when loadLibrary or load method in Runtime or System is called for a file that cannot be found.
You declare a Java method native
only if you're going to implement that method in C or C++ and then call it from Java. Since you're trying to do the opposite, i.e call Java methods from native code, you need to actually implement the get_property
method in Java. In native code you'll then create a class instance of TestObject
and call the get_property
method on this instance.
I found a Sun tutorial on how to embed the JVM in native code. The book itself begins with examples of how to call native code from Java.
Try this one: When you execute the Java application, add the missing link file with "LD_LIBRARY_PATH"
Something like
LD_LIBRARY_PATH=[the link file path need be included] java xxx.class
The path can use absolute path. Hope this might be helpful.
I think you should try writing the JNI function in another file. When you javah TestObject.java, a file TestObject.h will be generated. Create a file TestObject.c with the implemented function. Then build a shared library using the native code.
( Something like g++ -G -I/pkgs/jdk1.4/include TestObject.C -o libTestObject.so)
Also in TestObject.java, load the library statically like static{ System.loadLibrary("TestIbject");
The libTestObject.so should be added to LD_LIBRARY_PATH ( On a Linux environment)
精彩评论