In this sample we will pass integer into C code from Java. After you finish this tutorial, you should end up with the directory structure similar to this one
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
recipeNo002/ ├── Makefile ├── c │ ├── recipeNo002_PassInt.c │ └── recipeNo002_PassInt.h ├── java │ └── recipeNo002 │ └── PassInt.java ├── lib │ ├── libPassInt.dylib │ └── libPassInt.dylib.dSYM │ └── Contents │ ├── Info.plist │ └── Resources │ └── DWARF │ └── libPassInt.dylib └── target └── recipeNo002 └── PassInt.class |
First of all, we need a Java class that will call native code. That’s fairly simple
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* PassInt.java */ package recipeNo002; public class PassInt { /* This is the native method we want to call */ public static native void displayInt(int value); /* Inside static block we will load shared library */ static { System.loadLibrary("PassInt"); } public static void main(String[] args) { /* This message will help you determine whether LD_LIBRARY_PATH is correctly set */ System.out.println("library: " + System.getProperty("java.library.path")); /* Call to shared library */ PassInt.displayInt(1); } } |
Notice the difference comparing to HelloWorld sample. Above, in native method we declare that we will pass int value. You can also tell the difference in static block. This time, we load PassInt library. In real world applications, most of the native code will be stored in the same library. Here, I will put each function into separate file.
Again, we have to compile the class and create native’s code header
1 2 3 |
${JAVA_HOME}/bin/javac -h c -d target java/recipeNo002/PassInt.java |
After header is created
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class recipeNo002_PassInt */ #ifndef _Included_recipeNo002_PassInt #define _Included_recipeNo002_PassInt #ifdef __cplusplus extern "C" { #endif /* * Class: recipeNo002_PassInt * Method: displayInt * Signature: (I)V */ JNIEXPORT void JNICALL Java_recipeNo002_PassInt_displayInt (JNIEnv *, jclass, jint); #ifdef __cplusplus } #endif #endif |
we can create implementation of the function and compile the code
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <stdio.h> #include "jni.h" #include "recipeNo002_PassInt.h" JNIEXPORT void JNICALL Java_recipeNo002_PassInt_displayInt (JNIEnv *env, jclass obj, jint value) { printf("Passed value: %d\n", value); } |
Compilation is plain and simple
1 2 3 |
cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(ARCH) c/recipeNo002_PassInt.c -o lib/libPassInt.dylib |
After all elements are in place, all we have to do is to run the sample
1 2 3 |
${JAVA_HOME}/bin/java -Djava.library.path=${LD_LIBRARY_PATH}:./lib -cp target recipeNo002.PassInt |
1 2 3 4 5 6 7 |
${JAVA_HOME}/bin/java \ -Djava.library.path=:./lib \ -cp target recipeNo002.PassInt library: :./lib Passed value: 1 |
As you can see, we can read what was passed to native code as it was written to stdout. And that’s what we wanted to get.