Falls nur C-Source-Code eines Programmes vorhanden ist, dieser aber über Java nutzbar gemacht werden soll, bietet sich in JNI eine Möglichkeit dazu. Die Idee dieses Tutorials ist ein "Durchstich" um das Prinzip zu verstehen und die Fallstricke zu kennen. In meinem Beispiel verwende ich Eclipse Helios und Java 6 unter Windows XP. Das Tutorial mit den Beispieldateien findet sich hier
(diesen Schritt überspringe ich, da einfach bzw. ausreichend dokumentiert)
Damit unter Eclipse mit C entwickelt werden kann benötige ich:
2a: CDT-Plugin-Installation
2b: MinGW-Installation
Folgende Packages habe ich manuell hier heruntergeladen:
2c: Eclipse neu starten, Die Übung kann beginnen.
starte Eclipse, dann "file->new->project->java-project" nennen wir es "JniCall"
kreiere im "src"-ordner neue Klasse "Main" (der Einfachheit halber machen wir keine Packages)
folgender sourcecode eingeben:
public class Main { public static native void CLibSaysHello(); //Deklaration der C-Methode static //einmaliger Aufruf durch den ClassLoader der JVM zur Laufzeit { System.loadLibrary("libJniDLL"); } public static void main(String[] args) { System.out.println("java says hello"); CLibSaysHello(); } }
kompilliere unter project -> Build Project
Nun starte eine cmd-konsole und wechsle ins verzeichnis "Laufwerk:\my_Eclipse_Workspace\JniCall\bin". Es findet sich dort eine "Main.class"
gib ein (je nach installation des JDK kann das Verzeichnis anders lauten):
c:\Programme\Java\jdk1.6.0_22\bin\javah.exe -o jniHeader.h Main
Das erzeugt ein C-Headerfile, das wie folgt aussieht:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Main */ #ifndef _Included_Main #define _Included_Main #ifdef __cplusplus extern "C" { #endif /* * Class: Main * Method: CLibSaysHello * Signature: ()V */ JNIEXPORT void JNICALL Java_Main_CLibSaysHello (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
soweit, so gut, lassen wir Java ruhen und machen jetzt C
Nun beginne ein C-projekt und zwar eine DLL:
wähle im nächsten Reiter:
kreiere ausserdem einen source-folder mit Namen "src" im Projekt
jetzt folgen wichtige Projekteinstellungen:
typedef __int64 jlong;mit:
#ifdef __GNUC__ typedef long long jlong; #else typedef __int64 jlong; #endifDas ist nötig weil der GCC den Datentyp "__int64" nicht versteht und Error meldet
Damit sind die Einstellungen beendet und die Sources können kreiert werden:
kopiere aus dem Java-Projekt das headerfile "jniHeader.h" in den source-folder
kreiere ein source-file "main.c" im gleichen folder und implementiere folgenden Code
#include "jniHeader.h" #include <stdio.h> JNIEXPORT void JNICALL Java_Main_CLibSaysHello (JNIEnv * env, jclass class) { printf("C says Hello"); }
nun kann eclipse ohne Probleme kompillieren und kreiert ein File genannt "libJniDLL.dll"
kopiere diese ".dll" nun ins Projekt-Root-Verzeichnis des Java-Projekt "JniCall", also nach:
"Laufwerk:\my_Eclipse_Workspace\JniCall\"
(Es ist wichtig, dass die DLL in den richtigen Java Library-Path kopiert wird weil Eclipse diesen so definiert und die JVM zur Laufzeit dort die DLL sucht.)
Starte nun Main.java mit "rechter Maustaste -> Run as -> Java Application" und geniesse den Konsolen-Output:
java says hello C says Hello
Nun wollen wir das ausserhalb von Eclipse laufen lassen.
rechts-Klick im Projekt "JniCall"
wähle im nächsten Reiter:
kopiere "libJniDLL.dll" in den gleichen Ordner wo das Jar liegt
öffne eine cmd-konsole und gib ein:
java -jar jni.jar
Voila!!
nachdem es nun unter Windows funktioniert nehmen wir Ubuntu. Das geht derart gut, dass wir nicht mal Eclipse dazu brauchen. Ich empfehle die C-Sources unter Linux zu kompillieren, da ein Cross-Compiling unter Windows keine Binaries im ELF-Format erzeugt
Falls "openjdk" noch nicht installiert ist, öffne eine Shell und gib ein:
sudo apt-get install openjdk-6-jre
Während der Installation ist man vom Internetzugang abhängig wie ein Junkie von der Heroin-Spritze. Offline-Installationen sind in der Ubuntu-Welt leider immer noch kompliziert.
Kopiere die Sources "Main.java", "jniHeader.h" und "main.c" an beliebigen Ort (zB /root/Arbeitsfläche/jni)
starte eine shell und wechsle dahin
cd /root/Arbeitsfläche/jni
Kompillieren und Linken der C-Sources
gib nacheinander folgende Kommandos ein:
gcc -I/usr/lib/jvm/java-6-openjdk/include -I/usr/lib/jvm/java-6-openjdk/include/linux -O0 -g3 -Wall -c -fmessage-length=0 -omain.o main.c gcc -shared -oliblibJniDLL.so main.o
Wichtiger Hinweis: Die JVM verwendet unter Linux ein "lib" als Such-Präfix und ".so" als Such-Suffix. Unter Linux wird also nach "liblibJniDLL.so" gesucht. Unter Windows wird analog nach "libJniDLL.dll" gesucht, also nur ein ".dll" als Such-Suffix
Zum Kompillieren der Java-Klasse gib ein:
javac Main.java
benötigt werden nur "Main.class" und "liblibJniDLL.so". Teste nun mit
java -Djava.library.path="./" Main
Anmerkung: Die Djava-Option braucht es, damit die JVM das .so File im richtigen Pfad sucht und findet.
Ich hoffe die Einführung hat Spass gemacht