Again, here's the code for our C program:
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
/* There is a new JNI_VERSION_1_4, but it doesn't add anything for the purposes of our example. */
args.version = JNI_VERSION_1_2;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=c:\\projects\\local\\inonit\\classes";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "example/jni/InvocationHelloWorld");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "From-C-program");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
int main(int argc, char **argv) {
JNIEnv* env = create_vm();
invoke_class( env );
}
In this section, we'll look at the invoke_class(JNIEnv*)
function.
The JNIEnv
pointer contains most of the functions for interacting with Java programs from the native
side. A complete (as of JDK 1.1) list of JNIEnv
functions is available in the
JNI specification; there are
some JDK 1.2 enhancements described in the JDK 1.2
JNI Enhancements document and even newer
enhancements in the JDK 1.4 JNI Enhancements document.
(Yes, they could stand to rewrite and consolidate these; you currently have to read them all to know everything that's
available.)
The process for invoking the main
method of our InvocationHelloWorld
class is similar to
using Java reflection.
Our first task is to get a reference to the class we want to execute. We can do this via the FindClass
function. Note that an extra level of indirection is necessary when using C; in C++, we could code the
FindClass
call as env->FindClass("example/jni/InvocationHelloWorld");
. Note also that we use VM-style syntax
for indicating the class name -- using slashes rather than periods for delimiting packages, etc.
Next, we want to get a reference to the main
method with the signature
static void main(java.lang.String[])
. (Note that JNI does not use access modifiers like
public
, private
, etc., so JNI breaks Java encapsulation.) In order to do that, we need to translate
the method signature into the VM's mangled language. The messy details on this are also in the
JNI specification (as well as the
VM specification). In any case, void [method name](String[])
becomes ([Ljava/lang/String;)V
.
Using the method's name and signature as arguments, we can retrieve a jmethodID
representing the method
using GetStaticMethodID
.
Now, we need to create a String
array to pass as an argument to the main
method. An
array (of objects; primitive arrays have their own functions) can be instantiated using the
NewObjectArray
function and specifying the array's type
(we get a reference to the String
class using FindClass
again) and length (the last
argument specifies the "default value" for the array -- that is, the value to which all the elements will be set upon
initialization).
To specify our argument -- the string "From-C-Program"
-- we need to create a String
object and then
set the array's element to point to it. Mercifully, JNI includes some string manipulation functions to make the
conversion between normal C strings and Java Unicode String
s easier. One such function is the
NewStringUTF
function, which allows the creation of a Java String
given a UTF-8 string.
We create our String
using it and then set our array's element using SetObjectArrayElement
.
Finally, we invoke the main
method using our class reference, our method reference, and our arguments
array.