Java Native Interface (JNI)

Introduction

Hey! Did you know Java was made to compensate for the “setbacks” of C++? In a nutshell, Java is just a glorified version C++, just like how C++ is a glorified C!

So how can we use the C and C++ code we all know and love inside of Java? Well, the good people at Sun Microsystems have given us a little gem of a utility called the Java Native Interface (AKA, JNI) so we can rather easily use C and C++ to our advantage.

Why?

What!? What do you mean “What advantage to C and C++!?” You crazy kids these days…

But it does bring up a question as to “why interface back to C and C++ when we’re developing on a platform that corrects their drawbacks?” Well, Java has its own drawbacks that C and C++ do not – like speed. Java is running on a virtual machine (thats what helps it be so cross-platform). The execution of commands are going to take a hit just because of the extra layers of JVM that the code needs to pass through. C and (a good portion of) C++ have very close to machine-code commands. They are able to talk more directly to hardware and memory, and are faster in execution. By using JNI to call C and C++ functions, you have access to that speed!

Another good thing about using JNI is your ability to reuse legacy code. You made that one object manager and that OpenGL renderer – why write them again? JNI lets you use your old wheels so you don’t have to re-invent them.

And finally, if you’re an old C/C++ die-hard that’s being forced to upgrade, this makes the transition a little easier.

Create the Java Interface Class

OK, ready for this? This is the easiest part. In your Java program, write:

public native void Nfunction();

That’s it. Seriously. Just create a bunch of function prototypes and include that native keyword and you’re golden! You can make them any way you’d like; give them return values, a few parameters here and there, but I will recommend this: stick to primitive types. stick with ints, floats, strings, chars, booleans – try not to pass anything you made, or you’ll be working your butt off trying to access that data. Trust me. Make it easier on yourself and stick to primitives until you feel you’ve become a JNI Master.

Create the C/C++ Library

Now for the C/C++ part. A nice thing about JNI is that Java will actually generate half the code for you! Java will create a header file that can work with C and C++, and all by a single command! To do this:

  1. Open up a command window (cmd, PuTTY, etc) and go to your codes directory.
  2. Compile your java file to make a class file.
  3. Type this in:  javah -jni yourClassNameHere

Give it a second, and java will create a header file in the directory you called javah from.

Note: If you’re using Netbeans, this becomes easier for you. Just download this JNI plugin and you can generate a header file on the spot!

Now we have a header file, have a peek inside and you’ll see includes, #defines, extern “C”, #ifdef _cplusplus and the whole lot, and you’ll also see your native functions…

… or they kinda look like your functions…

What happens is Java wraps them in library export symbols that are usually specific to your platform, but by wrapping platform-specific function symbols in #defines, it allows us to easily transport the code from one system to another.

So now our functions look like:

JNIEXPORT jreturnType JNICALL Java_packages_className_functionName(JNIEnv *, jobject, jparameters);

Looks pretty funky, but you can see what’s going on. I’ll explain a few things before we continue. JNIEXPORT, JNICALL – These wrap the function in library export symbols like you would for any library functions. They’ve been redefined with #defines to make them cross-platform. jreturnType and jparameters – Java has type definitions set up for different types. The group listed below are all the primitive types and their native equivalents.

  • Java Type: jboolean
    • Native Type: unsigned char
    • Desc: unsigned 8 bits
    • Signature: Z
  • Java Type: jbyte
    • Native Type: signed char
    • Desc: signed 8 bits
    • Signature: B
  • Java Type: jchar
    • Native Type: unsigned short
    • Desc: unsigned 16 bits
    • Signature: C
  • Java Type: jshort
    • Native Type: short
    • Desc: signed 16 bits
    • Signature: S
  • Java Type: jint
    • Native Type: long
    • Desc: signed 32 bits
    • Signature: I
  • Java Type: jlong
    • Native Type: long long __int64
    • Desc: signed 64 bits
    • Signature: J
  • Java Type: jfloat
    • Native Type: float
    • Desc: 32 bits
    • Signature: F
  • Java Type: jdouble
    • Native Type: double
    • Desc: 64 bits
    • Signature: D

There are others, however, and they need to be treated differently. Take, for example, a Java String, or a Java-style Array. You can’t simply just pass the pointers back and forth between Java and C/C++. That’s where our next bullet point comes in.

JNIEnv *, jobject – the JNIEnv* (lets call him “env”) is a structure containing everything necessary to interface Java with C/C++. Using env, you’ll be able to convert a char* in C/C++ into a java.lang.String in Java, as well as plenty of other things like setting up arrays, accessing Java-specific parameter types, and even call functions from Java.

Like I said, though, I’m keeping this basic.

Note: C++ tends to be “cleaner” with the JNI stuff than C. Some of the functions for env and even its general usage will vary slightly between the two.

Now that we have our header, it’s up to you to give it a corresponding C/C++ source file and fill out the functions. Once you have that, compile your C/C++ code into a library (if you’re unsure how, you’re going to have to research a bit on how to compile a library for your platform. Just make sure you include the directory to jni.h in your compile (something like <JAVADIR>/include)).

Interface!

Now we are tasked with linking our C/C++ library with Java. Once again, it’s nice and easy. In the Native interface class we made we can make a static initializer simply with:

// Statically load the library
static
{
System.load("<LibraryFullDirectory>");
}

You can also use System.loadLibrary(), but I like to be explicit (especially when I know the library is all going to the same spot).

Run and Enjoy

Alright! Everything filled out and linked up? Then have at thee! Give it a run! It should be pretty basic on what you have to do, however JNI is not without its fallacies. Because Java doesn’t know what is happening in the C/C++ library, if there is an error, 1- you’ll crash the JVM. Hard. And 2- you’ll have a fun time trying to debug that beast. This is why I said use the primitive Java and C/C++ types. Another error you’ll find if you didn’t link up everything correctly is an UnsatisfiedLinkError from Java. The best method to resolving this is going back over your code to see if you missed a function, or to regenerate the header file again.

So, pretty much that’s JNI in a nutshell. There are a lot of good tutorials and examples out there for JNI. I’ll list a few just to get the ball rolling. Thanks for reading!

Links

SDN JNI Example

Matthew Mead – Programming in C/C++ with the Java Native Interface

Sun Forums – JNI – For any questions or problems you may have. Great people there. You may still be able to find my post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s