[Logo der Universität Bayreuth]
Universität Bayreuth

Mathematisches
Institut



 Einleitung

 Erste Schritte

 Mail und News

 Drucken

 KDE

 LaTeX/TeX

 Linksammlung

 Linuxtools

 Netzwerk

 Programmieren

 Windows

 X Window

 Anträge

 Kontakt

Java <-

Nutzung des Java Native Interface (JNI)

Im nun anschliessenden Text wollen wir uns mit der Verwendung des Native-Interfaces (Schnittstelle zu Funktionen/Programmen entwickelt in anderen Programmiersprachen) von Java beschäftigen. Es bietet dem Java-Programmierer die Möglichkeit, auf Features des Betriebsystems (oder der Betriebsystemsprache) zuzugreifen, welche in Java nicht angeboten werden. Die Liste solcher Features dürfte zu umfangreich ausfallen, weswegen hier auf eine Auflistung verzichtet wird.
Denkbar ist z.B. der Fall, dass ein neues Programm in Java entwickelt wird, das Funktionalität verlangt, die teilweise bereits als C-Code vorliegt. Nun kann es langwierig oder zumindest lästig sein, den ganzen alten C-Code nach Java umzuschreiben. Und genau für solche Fälle ist es gut, dass es das Java Native Interface (JNI) gibt.
Zunächst folgt eine allgemeine Anleitung zum Vorgehen und daraufhin wollen wir uns die Theorie anhand eines sehr einfachen Beispiels verinnerlichen.
Wir werden also sehen wie wir aus einer C/C++ Datei eine dynamische Library ("*.so"-Datei unter Linux bzw. eine "*.dll"-Datei unter Windows) und die darin enthaltenen Methoden aus einem Java-Programm aufrufen.
Unter jedem Punkt der allgemeinen beschriebenen Schritte gibt es einen Link zum jeweils konkreten Beispiel, in dem in der Shell ein Kommando aufgerufen wird.

  • Erzeugen der Java-Datei

    Zunächst erzeugen wir eine ganz gewöhnliche Java-Datei, die eine öffentliche (public) Klasse enthält. In dieser Klasse deklarieren wir nun eine public native-Methode mit beliebigem Namen (der Name unterliegt selbstverständlich allen sonst auch in Java bekannten Einschränkungen), z.B.

        public native void printline();
                        
    Darüberhinaus formulieren wir einen klassenweiten Ladeaufruf
        System.loadLibrary("mylib");
                        
    der noch zu erzeugenden Library namens "mylib.so" (Linux) bzw. "mylib.dll" (Windows).
    Nun fehlt nur noch eine main(..)-Methode, in der wir ein Objekt "caller" unserer neuen Klasse erzeugen und anschliessend durch "caller.printline();" die native Methode aufrufen.
    Screenshot: Java Datei

  • Erzeugen der Header-Datei

    Nun übersetzen wir die Klasse mit dem Java-Compiler (im Beispiel: "javac NativeCaller.java") und generieren anschließend eine für unsere Library notwendige Header-Datei (im Beispiel entsteht die Datei "NativeCaller.h"). Dazu nutzen wir das Tool javah aus dem J2SDK. Diesem Tool übergeben wir den Namen der soeben erzeugten Klasse (im Beispiel: "javah NativeCaller").
    Screenshots: Übersetzen und Header erzeugen

  • Erzeugen der C-Datei

    Zunächst legen wir eine neue C-Quellcode-Datei (im Beispiel "mylib.c") an. Wir müssen beim Schreiben der Funktionen bis auf wenige Details nichts Spezielles beachten, wir gehen also im Wesentlichen genauso vor wie wir es für jedes C-Programm machen.
    Mit Präprozessorincludes ergänzen wir die Header "jni.h" sowie das im Schritt zuvor von javah erzeugte Header-File (sowie natürlich alle anderen Headerfiles, die wir zur Realisierung unserer C-Funktion benötigen). Im Beispiel lauten diese:

      #include <jni.h>
      #include "NativeCaller.h"
                        
    Statt des normalen Funktionskopfes
      void printline(void)
                        
    verwendet man allerdings den durch javah erzeugten Funktionskopf, im Beispiel:
      JNIEXPORT void JNICALL Java_NativeCaller_printline(JNIEnv *env, jobject obj)
                        
    Was wir allerdings nicht benötigen, ist eine "main(..)"-Funktion in der C-Datei, da das Programm ja nicht selbstständig laufen soll, sondern nur Funktionen daraus aus einem anderen (bereits laufenden) Programm aufgerufen werden sollen.
    Screenshot: C-Datei
  • Erzeugen der dynamischen Library

    Wir übersetzen das eben erzeugte C-Programm in eine dynamische Bibliothek (kein Executable erzeugen, geht ohne main(..)-Funktion sowieso nicht!).

    • Unter Linux mit dem GCC-Compiler:
        # Man ersetze "$JAVA_HOME" durch das Installationsverzeichnis des J2SDK,
        # z.B. durch "/usr/java/j2sdk1.4.2_01",
        # und "srcname.c" durch den Namen des C-Sourcefiles sowie
        # "[libname]" durch den Namen der dynamischen Library
        gcc -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux srcname.c -o lib[libname].so
        # im Beispiel:
        gcc -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux mylib.c -o libmylib.so
                          
      Durch die Option "-shared" sagen wir dem Compiler, dass eine shared Library (so = shared object) erzeugen werden soll (im Beispiel "libmylib.so").
      Sollten für die Ausführung der Funktion/en der Bibliothek noch andere Bibliotheken benötigt werden, müssen diese natürlich schon hier dazugelinkt werden.
      Screenshot: Dynamische Lib erzeugen
    • Unter Windows mit dem frei verfügbaren cl-Compiler von Microsoft (Download z.B. über das Visual C++ Toolkit 2003):
        rem Man ersetze "%JAVA_HOME%" durch das Installationsverzeichnis des J2SDK,
        rem z.B. durch "C:\Programme\j2sdk1.4.2_01",
        rem und "srcname.c" durch den Namen des C-Sourcefiles sowie
        rem "[libname]" durch den Namen der dynamischen Library
        cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD srcname.c -Fe [libname].dll
        rem Im Beispiel:
        cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD mylib.c -Fe libmylib.dll
                          

  • Starten der Java-Applikation

    Jetzt brauchen wir unser Java-Programm nur noch auszuführen. Dem Java-Interpreter übergeben wir dabei folgende Option "-Djava.library.path=." (neben dem Klassennamen selbst natürlich ;-) ).
    Dadurch teilen wir dem Interpreter mit, dass das aktuelle Verzeichnis nach dynamischen Libraries durchsuchen soll. Und das erzeugte Libraryfile "*.so"/"*.dll" liegt ja im aktuellen Verzeichnis. Im Beispiel lautet der Befehl:

      java -Djava.library.path=. NativeCaller
                        
    Wichtig ist hier zu erwähnen, dass man dieses Argument auch dann nicht weglassen kann, wenn das aktuelle Verzeichnis "." im CLASSPATH (Environmentvariable für den Suchpfad von Java-Klassen) enthalten ist.

    Unter Linux reicht es auch, die Environmentvariable LD_LIBRARY_PATH zu setzen und die obige Option für java wegzulassen:

      # Falls Environmentvariable LD_LIBRARY_PATH leer oder nicht vorhanden war:
      export LD_LIBRARY_PATH="."
      # ansonsten:
      export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:."
      java NativeCaller
                        


    Screenshot: Java-Programm starten

Wenn die Funktion in C++ statt in C realisiert wird, geht man analog vor.

top top

Downloads

Files für das 1. Beispiel:
Java-File,
C-File,
erzeugtes C-Header-File

Variante des 1. Beispiels mit static-Methode:
Java-File, C-File, erzeugtes C-Header-File
Änderungen in der Beschreibung:
Deklaration der C-Funktion in Java-Applikation:

    public static native void printline();
                
Funktionskopf der C-Funktion im C-Sourcecode:
  JNIEXPORT void JNICALL Java_NativeCallerStatic_printline(JNIEnv *env, jclass cl)
                
Der Aufruf der nativen C-Funktion ändert sich in "printline();" (es wird kein Objekt der Java-Applikationsklasse mehr gebraucht).

2. Beispiel mit Funktionsargumenten:
Im gzip-ten tar-Archive "jni_ex_02.tar.gz" findet man ein weiteres Beispiel, in dem der nativen Funktion ein String und ein int-Wert übergeben werden. Der int-Wert steht dabei für die Anzahl, wie oft der übergebene String in der Funktion ausgegeben werden soll.

3. Beispiel mit Funktionsrückgabe:
Im gzip-ten tar-Archive "jni_ex_03.tar.gz" findet sich ein Beispiel, in dem die native Funktion einen String (den sie vorher vom User einliest) an das aufrufende Java-Programm zurückgibt.

top top

Verbesserungsvorschläge, Fragen und Anregungen an
Robert Baier ([e-mail-Adresse von Robert Baier])
© 2004 Robert Baier, Manuel Doss
[Seitenzähler] Last modified: 22.07.2015