Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Java Native Interface Eine Einführung. 2 Motivation Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit (Sandbox-Prinzip)

Ähnliche Präsentationen


Präsentation zum Thema: "Java Native Interface Eine Einführung. 2 Motivation Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit (Sandbox-Prinzip)"—  Präsentation transkript:

1 Java Native Interface Eine Einführung

2 2 Motivation Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit (Sandbox-Prinzip) - verminderte Performanz (mit heutigen JIT-Compilern i.A. kein Problem mehr) - kein direkter Zugriff auf Hardware-/Betriebssystem C/C++: Programmiersprache für OS-Entwicklung (UNIX) + maschinennah, hohe Performanz + alt, weit verbreitet, umfangreiche Sammlung von Legacy-Code - Programmierung möglicherweise umständlich (GUI in C?) - hohe Anforderungen an Programmierer

3 3 Das Java Native Interface (JNI) Schnittstelle, um C/C++ -Code in Java einzubinden und umgekehrt Beispiele - C/C++ in Java: Treiber, Betriebssystemfunktionen ansprechen (SWT) Vorhandene Bibliotheken einbinden Zeitkritische Funktionen in C oder Assembler einbinden Möglicherweise Verlust der Portabilität! Beispiel – Java in C/C++: Browser, der Applets ausführt

4 4 JNI - Architektur Schnittstelle definiert Datentypen und Funktionen… zum Aufruf nativer Funktionen in Java zur Abbildung von Java-Datentypen in C/C++ zur Erzeugung und Mani- pulation von Java-Objekten in C/C++ (OO in C?) zum Aufruf einer JVM aus C/C++ heraus

5 5 C in Java : Hallo Welt Minimal-Beispiel: Aufruf einer nativen Methode, die Hallo Welt! auf der Konsole ausgibt. Prinzipielles Vorgehen (immer gleich):

6 6 Hallo Welt – Java-Code Definition einer nativen Methode ohne Rumpf Dynamisches Laden der Bibliothek, die Methode bereitstellt Kompilieren mit: javac HelloWorld.java class HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } } class HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } }

7 7 Hallo Welt – C/C++-Header Die C-Methode, die in HelloWorld aufgerufen wird, muss gewisse Konventionen erfüllen javah –jni HelloWorld.class erzeugt Header-Datei HelloWorld.h mit Prototypen aller nativen Methoden: Methoden-Name muss in C eindeutig sein, daher: Java_packageName_className_methodName Parameterlose Funktion print() erhält in C zwei Parameter !? include […] JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject); […] include […] JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject); […]

8 8 Hallo Welt – C-Code Implementierung der in Header-Datei definierten Funktion in Datei HelloWorld.c : Übersetzen in gemeinsam genutzte Bibliothek gcc -fPIC -c HelloWorld.c ld -shared -soname libHelloWorld.so.l -o libHelloWorld.so HelloWorld.o #include "HelloWorld.h" #include JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj){ printf("Hallo Welt!\n"); } #include "HelloWorld.h" #include JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj){ printf("Hallo Welt!\n"); }

9 9 Hallo Welt - Finale Ausführen des Java-Programms: java HelloWorld java.lang.UnsatisfiedLinkError: no HelloWorld in library path at java.lang.Runtime.loadLibrary(Runtime.java) at java.lang.System.loadLibrary(System.java) at HelloWorld.main(HelloWorld.java) Pfad zur Bibliothek noch unbekannt! Entweder 1.Bibliothek in /usr/lib verschieben (root-Rechte!), 2.setenv LD_LIBRARY_PATH.:${LD_LIBRARY_PATH}, 3.oder Bibliothekpfad explizit an Java übergeben: java –Djava.library.path=. HelloWorld

10 10 Abbildung der Datentypen Bisher: kein Datenaustausch zwischen Java und C Datentypen in Java: Primitive Datentypen, Klassen, Objekte, Arrays Unterschiede Java vs. C: Java: Einheitliche Spezifikation primitiver Datentypen auf allen Plattformen C: plattformabhängig (z.B. 2Byte int, 4Byte int) Java: Objektorientierung C: keine Objekte, aber strukturierte Datentypen und Funktionszeiger

11 11 Abbildung primitiver Datentypen Java-TypNativer TypBeschreibung booleanjboolean8 Bit ohne Vorzeichen bytejbyte8 Bit mit Vorzeichen charjchar16 Bit ohne Vorzeichen shortjshort16 Bit mit Vorzeichen intjint32 Bit mit Vorzeichen longjlong64 Bit mit Vorzeichen floatjfloat32 Bit doublejdouble64 Bit void

12 12 Abb. primitiver Datentypen – Beispiel public class Sum{ static{ System.loadLibrary("Sum"); } public static native int sum(int a, int b); public static void main(String[] args){ System.out.println("sum(3,4)=" + sum(3,4)); }} public class Sum{ static{ System.loadLibrary("Sum"); } public static native int sum(int a, int b); public static void main(String[] args){ System.out.println("sum(3,4)=" + sum(3,4)); }} #include […] JNIEXPORT jint JNICALL Java_Sum_sum (JNIEnv *env, jclass, jint, jint); […] #include […] JNIEXPORT jint JNICALL Java_Sum_sum (JNIEnv *env, jclass, jint, jint); […] JNIEXPORT jint JNICALL Java_Sum_sum(JNIEnv *env, jclass cl, jint a, jint b){ jint result = a + b; return result; } JNIEXPORT jint JNICALL Java_Sum_sum(JNIEnv *env, jclass cl, jint a, jint b){ jint result = a + b; return result; }

13 13 Abbildung von Referenz-Datentypen Objekte, Klassen und Arrays werden in Java über Referenzen angesprochen An C wird ein transparenter Pointer übergeben, der auf die Objekte in der JVM zeigt. Zugriff auf die Objekte erfolgt immer über die JNIEnv-Umgebung, die jeder nativen Methode übergeben wird:

14 14 Abb. von Referenztypen – z.B. Arrays import java.util.Random; public class Sort{ static{ System.loadLibrary("QSort"); } public static native void sort(double[] arr); public static void main(String[] args){ […] double[] arr = new double[Integer.parseInt(args[0])]; Random r = new Random(); for(int i=0; i< arr.length; arr[i++] = r.nextInt(10000)); long s = System.currentTimeMillis(); sort(arr); long e = System.currentTimeMillis(); System.out.println("Sortierung dauerte "+(e-s)+"ms"); } import java.util.Random; public class Sort{ static{ System.loadLibrary("QSort"); } public static native void sort(double[] arr); public static void main(String[] args){ […] double[] arr = new double[Integer.parseInt(args[0])]; Random r = new Random(); for(int i=0; i< arr.length; arr[i++] = r.nextInt(10000)); long s = System.currentTimeMillis(); sort(arr); long e = System.currentTimeMillis(); System.out.println("Sortierung dauerte "+(e-s)+"ms"); }

15 15 Abb. von Referenztypen – z.B. Arrays Prototyp: Zugriff auf Array-Elemente: JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass, jdoubleArray); JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass, jdoubleArray); JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){ jdouble *cArray = (jdouble*) arr; // FALSCH!!! // arr ist lediglich ein Pointer auf eine JVM- // Datenstruktur. Der Zugriff auf die Array-Elemente // muss über die JNIEnv Umgebung erfolgen! […] } JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){ jdouble *cArray = (jdouble*) arr; // FALSCH!!! // arr ist lediglich ein Pointer auf eine JVM- // Datenstruktur. Der Zugriff auf die Array-Elemente // muss über die JNIEnv Umgebung erfolgen! […] }

16 16 Abb. von Referenztypen – z.B. Arrays int compare(const void *a, const void *b){ return *((jdouble*) a) < *((jdouble*) b) ? -1 : +1; } JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){ jboolean isCopy; jdouble* cArray = (*env)->GetDoubleArrayElements(env, arr, isCopy); jint length = (*env)->GetArrayLength(env, array); qsort(cArray, length, sizeof(jdouble), &compare); if(isCopy) (*env)->ReleaseDoubleArrayElements(env, array, cArray, 0); } int compare(const void *a, const void *b){ return *((jdouble*) a) < *((jdouble*) b) ? -1 : +1; } JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){ jboolean isCopy; jdouble* cArray = (*env)->GetDoubleArrayElements(env, arr, isCopy); jint length = (*env)->GetArrayLength(env, array); qsort(cArray, length, sizeof(jdouble), &compare); if(isCopy) (*env)->ReleaseDoubleArrayElements(env, array, cArray, 0); }

17 17 Array-Zugriffsmethoden Get ArrayRegion, Set ArrayRegion Kopiert Bereich aus oder in ein Array mit primitivem Datentyp Get ArrayElements, Release ArrayElements Erhält einen Pointer auf ein Array (ggf. auch Kopie) und gibt ihn wieder frei (ansonsten Speicherleck!) GetArrayLength Ermittelt die Länge eines Arrays New Array Legt ein neues Array an Nur auf primitive Arrays anwendbar!

18 18 Zugriff auf Objekt-Arrays Beispiel: String[] strArray; int[][] intArray; Object[] objArray; Unmöglich, gesamtes Array abzufragen Lediglich einzelne Einträge zugreifbar GetObjectArrayElement SetObjectArrayElement

19 19 Beispiel: Anlegen eines 2D-Feldes Zuerst: Anweisung an JVM, ein int[] array anzulegen: private static native int[][] initInt2DArray(int size); JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass c, int s){ jobjectArray result; int i; jclass intArrCls = (*env)->FindClass(env, "[I"); if (intArrCls == NULL) return NULL; /* exception thrown */ result = (*env)->NewObjectArray(env, size, intArrCls, NULL); if (result == NULL) return NULL; /* out of memory error thrown */ … JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass c, int s){ jobjectArray result; int i; jclass intArrCls = (*env)->FindClass(env, "[I"); if (intArrCls == NULL) return NULL; /* exception thrown */ result = (*env)->NewObjectArray(env, size, intArrCls, NULL); if (result == NULL) return NULL; /* out of memory error thrown */ …

20 20 Beispiel: Anlegen eines 2D-Feldes Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; }

21 21 Beispiel: Anlegen eines 2D-Feldes Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Inhalt von tmp nach iarr kopieren

22 22 Beispiel: Anlegen eines 2D-Feldes Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Array an übergeordnetes Objekt-Array result übergeben

23 23 Beispiel: Anlegen eines 2D-Feldes Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } … for (i = 0; i < size; i++) { jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Kontrolle über iarr an JVM geben

24 24 Arbeiten mit Strings Java Strings sind Unicode-Codiert, C-Strings sind ASCII-codiert Anbindung über UTF-8 (kann beide Codierungen abbilden) GetStringChars, ReleaseStringChars GetStringUTFChars, ReleaseStringUTFChar GetStringLength, GetStringUTFLength NewString, NewStringUTF GetStringRegion, SetStringRegion GetStringUTFRegion, SetStringUTFRegion Anbindung sehr umständlich, Java im Umgang mit Strings komfortabler, daher wenig Relevanz

25 25 Zugriff auf Objekt-Felder und -Methoden Bei komplexen Klassen-Instanzen kann aus C heraus auf die Felder und Methoden der Instanz zugegriffen werden. Mechanismus erscheint relativ umständlich, daher hier nur exemplarisch dargestellt. Für nähere Infos siehe JNI-Referenz

26 26 Zugriff auf Static-Feld Java-Klasse mit statischem Feld si si soll in nativer Methode manipuliert werden class StaticFieldAccess { private static int si; private native void accessField(); public static void main(String args[]) { StaticFieldAccess c = new StaticFieldAccess(); StaticFieldAccess.si = 100; c.accessField(); System.out.println("In Java:"); System.out.println(" StaticFieldAccess.si = " + si); } static { System.loadLibrary("StaticFieldAccess"); } } class StaticFieldAccess { private static int si; private native void accessField(); public static void main(String args[]) { StaticFieldAccess c = new StaticFieldAccess(); StaticFieldAccess.si = 100; c.accessField(); System.out.println("In Java:"); System.out.println(" StaticFieldAccess.si = " + si); } static { System.loadLibrary("StaticFieldAccess"); } }

27 27 Zugriff auf Static-Feld JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj){ jfieldID fid; /* store the field ID */ jint si; /* Get a reference to objs class */ jclass cls = (*env)->GetObjectClass(env, obj); /* Look for the static field si in cls */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); if (fid == NULL) return; /* field not found */ /* Access the static field si */ si = (*env)->GetStaticIntField(env, cls, fid); printf(" StaticFieldAccess.si = %d\n", si); (*env)->SetStaticIntField(env, cls, fid, 200); } JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj){ jfieldID fid; /* store the field ID */ jint si; /* Get a reference to objs class */ jclass cls = (*env)->GetObjectClass(env, obj); /* Look for the static field si in cls */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); if (fid == NULL) return; /* field not found */ /* Access the static field si */ si = (*env)->GetStaticIntField(env, cls, fid); printf(" StaticFieldAccess.si = %d\n", si); (*env)->SetStaticIntField(env, cls, fid, 200); }

28 28 Zugriff auf Instanz-Methode Methode callback() soll in nativeMethod() gerufen werden class InstanceMethodCall { private native void nativeMethod(); private void callback() { System.out.println("In Java"); } public static void main(String args[]) { InstanceMethodCall c = new InstanceMethodCall(); c.nativeMethod(); } static { System.loadLibrary("InstanceMethodCall"); } class InstanceMethodCall { private native void nativeMethod(); private void callback() { System.out.println("In Java"); } public static void main(String args[]) { InstanceMethodCall c = new InstanceMethodCall(); c.nativeMethod(); } static { System.loadLibrary("InstanceMethodCall"); }

29 29 Zugriff auf Instanz-Methode JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj){ jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); if (mid == NULL) return; /* method not found */ printf("In C\n"); (*env)->CallVoidMethod(env, obj, mid); } JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj){ jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); if (mid == NULL) return; /* method not found */ printf("In C\n"); (*env)->CallVoidMethod(env, obj, mid); }

30 30 Das war erst der Anfang... JNI bietet noch mehr: Erzeugen von Objekten, Aufruf von Konstruktoren Caching von Methoden-/Feld-Ids Exception-Handling … Diese Themen werden dem geneigten Hörer zum Selbststudium überlassen

31 31 Fazit Enorme Möglichkeiten, positive Aspekte von C, C++ und Java zu integrieren Aufwand allerdings nicht unerheblich, daher sollten immer auch Alternativen in Betracht gezogen werden Für Standardaufgaben ist Einsatz von JNI aufgrund des stetig abnehmenden Performanz-Vorsprungs von C/C++ (wahrscheinlich) nicht lohnend


Herunterladen ppt "Java Native Interface Eine Einführung. 2 Motivation Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit (Sandbox-Prinzip)"

Ähnliche Präsentationen


Google-Anzeigen