A Java source file is compiled to a Java class file. This is a set
of instructions for the class which use instructions from the
Java Virtual Machine. The class file is interpreted by the Java
runtime engine (java
or jre
). The
interpreter is written in C, although other languages could have
been used.
Not everything can be written in Java. Certain operations in classes are not portable across all operating systems. Examples are
FileInputStream.read()
Runtime.totalMemory()
Thread.currentThread()
native
methods, meaning that the
implementation is done in C, not in Java.
The object code for native
methods are stored in dynamic
link libraries (.dll's in Windows, .so's in Unix). You need to be able
to match up the object code to the correct method. Diagramatically it
looks like
public class HelloWorld {
public native void sayHello();
}// HelloWorld
javac HelloWorld.java
javah -jni HelloWorld
The result is
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_sayHello(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The important part is the function prototype for
Java_HelloWorld_sayHello
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_sayHello(JNIEnv *env, jobject obj) {
printf("Hello world\n");
}
gcc -c -I/usr/local/src/jdk1.3/include \
-I/usr/local/src/jdk1.3/include/linux \
-fPIC hello.c
gcc -shared -W1 -o libhello.so hello.o
public class TestHello {
static {
System.loadLibrary("hello");
}
public static void main(String[] argv){
new HelloWorld().sayHello();
}
}// TestHello
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
java TestHello
Makefile
suitable for this is
# Set to where your JDK home is
JHOME = /usr/local/src/jdk1.3
INCLUDE = -I $(JHOME)/include -I $(JHOME)/include/linux
# Set this to something appropriate for building shared libraries
CFLAGS = $(INCLUDE) -fPIC -g
OBJS = hello.o
CLASSES = \
HelloWorld.class \
TestHello.class
# GNU Make rule to build Java class files
%.class: %.java
javac $<
# Override default rule for building .o files
%.o: %.c %.class
javah -jni $*
gcc -c $(CFLAGS) $<
all: $(CLASSES) libhello.so
libhello.so : $(OBJS)
gcc -shared -W1 -o libhello.1.0 $(OBJS)
ln -f libhello.1.0 libhello.so
clean:
rm -f *~ core *.o lib* *.class
C code written using JNI must also
Java type | C type |
---|---|
int | jint
|
boolean | jboolean
|
void | void
|
Java type | C type |
---|---|
Object | jobject
|
Class | jclass
|
String | jstring
|
array | jarray
|
Java type | C type |
---|---|
int[] | jintArray
|
boolean[] | jbooleanArray
|
Object[] | jobjectArray
|
From this, Java methods such as
class ClassName {
native String methodName(int n);
}
will generate a C prototype of
JNIEXPORT jstring JNICALL Java_ClassName_methodName(JNIEnv *env,
jobject this, jint n);
A very simplified form of the FileInputStream.read()
method is
JNIEXPORT jint JNICALL Java_FileInputStream.read(JNIEnv *env,
jobject this) {
char ret;
int fd, nread;
// get the file descriptor from a private field of the
// object
fd = ...;
nread = read(fd, &ret, 1);
if (nread == 0) { /* EOF */
return -1;
} else if (nread == -1) { /* error */
if (errno == EINTR) {
JNUThrowByName(env, "java/io/InterruptedIOException");
} else {
JNU_ThrowIOException(env, "Read error");
}
} else {
return ret;
}
}
The JNI has mechanisms to