Problem Statement
- Call C-native function from java code.
- Pass a message string from Java code to native function.
- Native function prints this message.
- Native function returns the same message to calling java method.
- java method prints this returned string.
Environment
- Java SDK 1.7.0
- Visual studio 10.0 as C compiler.
Steps to be followed:
- Write a java class (say HelloWorld.java) that declares the native method.
- Compile this java class(HelloWorld.class).
- Create a C header file using javah tool (HelloWorld.h).
- Implement the native method in C (HelloWorld.c).
- Compile this C implementaion and create a HelloWorld.dll file.
- Run the HelloWorld using java runtime interpreter.
Step 1 : Write HelloWorld.java
private native String print(String msg);
public static void main(String[] args) {
System.out.println("C function says " + new HelloWorld().print("JAVA"));
}
static {
System.loadLibrary("HelloWorld");
}
}
Step 2 : Compile HelloWorld.java
Run javac HelloWorld.java in command line. This generates HelloWorld.class file.
Step 3: Generate C-header file
Run javah -jni HelloWorld in command line. This generates HelloWorld.h file. This is a C-Header file contains the functions signature for native method. We will later see the content of this file.
Step 4: Implement the native method in HelloWorld.c
#include "jni.h"
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT jstring JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj, jstring msg)
{
const jbyte *str;
printf("Inside C funcion!\n");
str = (*env)->GetStringUTFChars(env, msg, NULL);
printf("Msg from Java layer %s\n",str);
return msg;
}
Step 5: Compile this class file to create a HelloWorld.dll file
Here C code is compiled using Visual studio. Other compilers (like gcc) might have a different syntax for compiling.
- Run vcvars.bat in command line to set up environment for visual studio tools.
- Run cl -I"C:\Program Files\Java\jdk1.6.0_32\include" -I"C:\Program Files\Java\jdk1.6.0_32\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll . This generates a HelloWorld.dll file. -LD option instructs compiler to gerenate a .dll file in place of .exe file.
Step 6: Run HelloWorld using java runtime interpreter
Run java HelloWorld.
Behind the scene
Let us start by inspecting HelloWorld.h . Open this file in some editor. Following are the important points to be noticed.
- This file includes jni.h . JNI provides the functionality to call native code and provide the types that is used in this header file. jni.h must be explicitly called included during compile time.
- This file contains prototype function for HelloWorld that's called in Java code. "Java_HelloWorld_print" is the prototype function. Naming convention is Java_<class_name>_<method_name>. This is also known as name mangling. This is important as using this java virtual machine links java calls to native method.
- First two arguments of this prototype function is always present even when no argument is passed to java call. The third argument in our case (and so on) is the argument which is passed to java method. in our case we have passed String to print method which gets mapped to jstring in Java_HelloWorld_print function.
- Now coming to first argument JNIEnv * . This is a pointer known as interface pointer . This interface pointer points to a pointer which again points a structure of pointers. This structure of pointers contains pointer to interface functions (or JNI functions). These functions are accessed as (*env)->GetStringUTFChars(env, msg, NULL), where GetStringUTFChars is the JNI function.
Now open HelloWorld.c in an editor. Following are the important point to be noticed.
- This C-file must include the generated header file, in our case HelloWorld.h
- It must includes jni.h
- print method's signature should be identical as defined in generated header file. This function returns jstring (String in java) to the calling Java method and receives a jstring (String in java) from the java call.
So to print a java string in C function we will use (*env)->GetStringUTFChars(env, msg, NULL) JNI function. This returns a jbyte * ,using this pointer we can print the string in native C. This native function returns jstring type message to the calling java method.