Our first step is to code-up our Java application and compile it. Our Java class will look something like this.
public class TestApplication {
public native int getNumber();
public static void main(String args[]) {
System.loadLibrary("TestApplication");
TestApplication test = new TestApplication();
System.out.println(test.getNumber());
}
}
The native keyword in the method declaration simply tells the Java compiler that the getNumber() method is implemented in some native library outside of this Java class. The code inside the main method simply loads the external library (which we'll create in a later step) and calls the native getNumber() method that is implemented in our C++ code.
Next, we need to create a C++ header file that will define our native functions. This header file is not the same header file you may already have in your C++ code. This header file will be created against our TestApplication.class file. To generate this header file, run the following command at the command line from within the same directory as your TestApplication.class file.
> javah TestApplication
The auto-generated .h file will look like this.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestApplication */
#ifndef _Included_TestApplication
#define _Included_TestApplication
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestApplication
* Method: getNumber
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_TestApplication_getNumber
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The next step is to create the .cpp file that will contain the implementation of our native getNumber() method. Typcially, with JNI development, this C++ code is a wrapper around existing C++ code that we just expose using JNI. In other words, we're not editing the existing C++ library, but instead creating an adapter that will then call our legacy C++ code. Here's the .cpp source file in its entirety.
#include "TestApplication.h"
JNIEXPORT jint JNICALL Java_TestApplication_getNumber
(JNIEnv *env, jobject obj) {
return 10;
}
All I'm doing is including the header file that we automatically generated in the previous step and our native method is returning an integer value of 10. This is a very trivial example, but I want to keep it simple for this tutorial. In a later post, I'll talk about the details of JNIEnv, how to pass C++ object instances around, why the JNI methods are named the way they are, etc.. But that deserves a discussion of its own.
Next, we need to create the shared library that we referenced in our Java application. Depending on which C++ compiler you're using, this may vary a little, but the following command will automatically create the shared library if you're using the cl compiler on Windows.
Here are a few things to keep in mind:
- The -I flag is just telling the C++ compiler to include those directories as include directories.You'll need to modify those to your Java install directory.
- Make sure you have write permission to the directory where you're trying to create the DLL. If you don't, you'll get an error. Here, I'm just creating it in the local directory
- Make sure that the C++ compiler you're using is the same architecture as your JVM (i.e. 32-bit or 64-bit).
cl -I"C:\jdk1.6.0_24\include" -I"C:\jdk1.6.0_24\include\win32" -LD TestApplication.cpp -FeTestApplication.dll
Now, if we run our Java program, we'll see the value from our native method being returned to our Java application and the value of 10 being printed to the console. In a later post, I'll talk more about the details of JNI and look at some examples that involve passing objects to our C++ application.
No comments:
Post a Comment