Java Call C function
Java is a higher level language and works in platform independent way. Java can run on Linux, Windows, Mac, Android and many platforms. Java does not provide any standard or class APIs to interact with platform software. However there are many situations where user may require to interface with system software. System softwares are written mainly with C language. So some common questions may arise like how to run c program through java? or how to load c library in java? or call dll in java? Java Native Interface or JNI is the interface to bridge between C and Java. This article is primarily a java native interface tutorial for a new java programmer.
JavaC and JRE
Java programs are written inside source files as plain text format. These files are compiled with the help of Java Compiler or known as javac executable. Java compiler do not compile the source to native platform binary but it prepares an intermediate executable code. Java compiler generates platform independent executable code known as Java Byte code or JBC. Java Byte code files are are stored in ".class" files. These are not native executables like C, C++ etc generates. So they cannot be executed directly. Java runtime environment or JRE is an executable which runs java byte code in any platform.
Java Native Interface/JNI
Java run time environment(JRE) executes Java byte Code(JBC) from class binaries in platform independent way. Sometimes java applications may need to access some of the system resources which are strictly platform dependent. Java language is limited to upper layer and cannot access this directly from its context. However there is a provision in Java to interface with native binaries. This interface is well known as Java Native Interface or JNI in short. JNI in the gateway between JAVA and native binaries, written in C/C++/VB or any other compiled languages.
Java Native Interface requires below three files.
- Java source file - Main application java file which calls C functions
- C header file - C function prototypes are declared here
- C source file - C source implementation of the native functions
Java JNI Code
We have JNIDemo class code with main() routine to demo this JNI application. We have three member functions.
- void HelloWorld();
- int CallC ();
- void SetMembers();
These three functions are declared with native keyword. Native keyword instructs compiler to link the functions even if the definition of HelloWorld(), CallC() and SetMembers() do not exist. This is similar to linking a function from library DLL/SO file. Java compiler puts these interfaces in import table or checks import table of DLL/so during runtime linking. At this point we have java application code but there is no implementation in native side of the library.
JNI library error
We try to execute this code we will get error as Java runtime will fail to load import library JNIDemo.dll or libJNIDemo.so.
>javac JNIDemo.java >java JNIDemo Unable to load JNIDemo library
JNI Interface generation
Now we will start implementing the native side of the library. We need a tool called JAVAH (C Header and Stub File Generator) JAVAH comes with Java Development Kit and can be executed in the same way as we execute comiler javac or java. Javah takes byte code file name (without .class extension) as argument. It process the binary and generates a C/C++ header file in the same name. Name of the header file in the same name.
JNI header file
Note carefully javah has generated three function prototype in C/C++.
- Java_JNIDemo_HelloWorld() is the equivalent JNI prototype of HelloWorld() in java.
- Java_JNIDemo_CallC() is the equivalent JNI prototype of CallC() in java. It is calling all arguments by value.
- Java_JNIDemo_SetMembers() is the equivalent JNI prototype of SetMembers() in java.
JNIEXPORT macro tells C/C++ compiler to export these functions from DLL/SO library so that java run time can import these calls. Additionally there are two extra arguments for each functions. First argument is of type JNIEnv which gives a pointer to Java Runtime Environment. JNIEnv provides many interfaces which native binary can utilize. Next argument is of type jobject which represents the object itself or the "this" pointer. We can access members of the object through this arguments. Next arguments are same as declared in java. jboolean, jbyte, jchar, jshort, jlong, jfloat, jdouble are type definition of unsigned char, signed char, signed short, short, int, long, float and double in C/C++. Now java treats strings as UNICODE thus jstring is not directly char * but conversions are possible in both the ways.
Call by reference in java is not possible but this is equivalent of Call by reference in java. It access and modify member variables of JNIDemo class object and thus can be treated as the equivalent of call by reference.
Implement JNI Native C/C++
Now we will implement these interfaces in a dynamic link library in Windows or in a shared library in Linux. We need to create a C/C++ source file and may need to copy this header file to the same solder. Here we created JNIDemo.c in the same folder where JNIDemo.h is located. We should include this file and implement the definitions. jchar, jshort, jlong, jfloat, jdouble are type definition of char, short, long, float and double in C/C++ thus we can used these directly. But this is not true for the strings. Java treats strings char char as UNICODE. There are two bytes for each characters. First character contains ASCII value and next one contains language id. jstring can be converted to UTF char* and vice versa. GetStringUTFChars() can be used to extract char* from Java strings. NewStringUTF() is used to allocate a new UNICODE string from UTF char*. ReleaseStringUTFChars() is used to release existing buffer. Here in Java_JNIDemo_CallC(), we are simply printing the values passed from Java in C/C++ context and directly returning to Java context. Java_JNIDemo_SetMembers() is not taking arguments. Every C++/Java function has inherent member called this pointer. This function uses this object to access all member variables. JNI can access and modify all the members. These are the steps to access/modify Java members.
- GetObjectClass() returns the class reference
- GetFieldID() returns field ID of the member from class reference
- GetXXXXField()/GetObjectField returns an instance of the member variable from object
- SetXXXXField()/SetObjectField can be used to set the member value
This is only a demo however in practical situation where system applications need to access system resources can open device files and can call mmap/ioctls/sysctl etc to interface deeper inside into system side.
JNI Block Diagram
Now we need to place this native library in the same folder where our Java Class binary located and can run. If we have more than one class We can also place this in the Java Runtime binary folder jre\bin if we want to access this from more than one java class located in different folder.
:>java JNIDemo Hello World! Calling by Value Arguments from Java z (boolean) = true b (byte) = J c (char) = K s (short) = 10 i (int) = 100 l (long) = 1000 f (float) = 1.200000 d (double) = 1.500000 str (string) = Hello From Java! Calling by Reference Java members (from C) z (boolean) = true b (byte) = C c (char) = D s (short) = 20 i (int) = 200 l (long) = 2000 f (float) = 2.200000 d (double) = 2.500000 str (string) = Hello from Java! End of Calling by Reference Java Members (changed) z (boolean) = false b (byte) = D c (char) = E s (short) = 10 i (int) = 100 l (long) = 1000 f (float) = 1.2 d (double) = 1.5 str (string) = Hello form C!
About our authors: Team EQA
You have viewed 1 page out of 248. Your C learning is 0.00% complete. Login to check your learning progress.
Learn on Youtube