Today I would like share my very first experience about JNI—Java Native Interface. The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by native applications and libraries written in other languages such as C, C++ and assembly. This post uses a Hello-world example to demonstrate the communication between Java and C on Mac OS X.

Create a Java Application

Firstly, create a Java application called App.java:

public class App {

  static {
    /*
     * Load native library at runtime.
     * - Windows: ___.dll
     * - Mac OS: lib___.jnilib
     * - Linux: lib___.so
     */
    System.loadLibrary("hello");
  }

  private native void sayHello();

  public static void main(String... args) {
    new App().sayHello();
  }

}

Note that: shared library hello is loaded during class initialization; method sayHello is native, so its behavior is unknown during the compilation phase, and only be defined in runtime. Now compile the Java file:

$ javac App.java

Generate Header File via javah

Generate a header file for C/C++ programs by running the javah utility on the class file:

$ javah App

And then a file called App.h is generated:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class App */

#ifndef _Included_App
#define _Included_App
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     App
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_App_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

As you can see, the method generated is called Java_App_sayHello, composed by language “Java”, class name “App”, and method name “sayHello”.

Create Implementation in C

Implement the say-hello solution in language C as following:

#include <jni.h>
#include <stdio.h>
#include "App.h"

// Implementation of native method sayHello() of App class
JNIEXPORT void JNICALL Java_App_sayHello(JNIEnv *env, jobject thisObj) {
  printf("Hello JNI.\n");
  return;
}

The solution includes the header file of JNI jni.h, the header file of standard input/output stdio.h, and the header file of the hello-world application App.h. The method signature matches the one in header file, generated by javah utility. And we are simply adding a pring function inside the method.

Compile the Implementation

Now the implementation is done. We need to compile it using GCC in two steps:

First step, compile the file App.c, and the output file is App.o. In the following command, the 1st flag -I includes the folder $JAVA_HOME/include where jni.h is stored, and the 2nd flag -I includes the OS X folder $JAVA_HOME/include/darwin where jni_md.h is stored (used by jni.h). And the output file is called App.o.

$ gcc -c -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include" -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin" App.c

Second step, link the output file App.o to shared library libhello.jnilib:

$ gcc -shared -o libhello.jnilib App.o

Run the Program

Now, everything is ready. Run the JNI program:

$ java App -Djava.library.path=.
Hello JNI.

And it works :)

References