View
106
Download
0
Category
Preview:
Citation preview
Java Native Interface
Eine Einführung
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
Das Java Native Interface (JNI)
Schnittstelle, um C/C++ -Code in Java einzubindenund 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
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
C in Java : Hallo Welt
Minimal-Beispiel: Aufruf einer nativen Methode, die „Hallo Welt!“ auf der Konsole ausgibt.
Prinzipielles Vorgehen (immer gleich):
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"); }}
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 <jni.h> […]
JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject); […]
8
Hallo Welt – C-Code
Implementierung der in Header-Datei definierten Funktion in Datei HelloWorld.c:
Übersetzen in gemeinsam genutzte Bibliothekgcc -fPIC -c HelloWorld.c
ld -shared -soname libHelloWorld.so.l -o libHelloWorld.so HelloWorld.o
#include "HelloWorld.h"#include <stdio.h>
JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj){ printf("Hallo Welt!\n");}
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! Entweder1. 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
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: ObjektorientierungC: keine Objekte, aber strukturierte Datentypen und Funktionszeiger
11
Abbildung primitiver Datentypen
Java-Typ Nativer Typ Beschreibung
boolean jboolean 8 Bit ohne Vorzeichen
byte jbyte 8 Bit mit Vorzeichen
char jchar 16 Bit ohne Vorzeichen
short jshort 16 Bit mit Vorzeichen
int jint 32 Bit mit Vorzeichen
long jlong 64 Bit mit Vorzeichen
float jfloat 32 Bit
double jdouble 64 Bit
void void
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));
}}
#include <jni.h> […]
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;}
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
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"); }}
15
Abb. von Referenztypen – z.B. Arrays
Prototyp:
Zugriff auf Array-Elemente:
JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass, jdoubleArray);
JNIEXPORT void JNICALLJava_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
Abb. von Referenztypen – z.B. Arrays
int compare(const void *a, const void *b){return *((jdouble*) a) < *((jdouble*) b) ? -1 : +1;
}
JNIEXPORT void JNICALLJava_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
Array-Zugriffsmethoden
Get<Type>ArrayRegion, Set<Type>ArrayRegion Kopiert Bereich aus oder in ein Array mit
primitivem Datentyp Get<Type>ArrayElements,
Release<Type>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<Type>Array Legt ein neues Array an
Nur auf primitive Arrays anwendbar!
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
Beispiel: Anlegen eines 2D-Feldes
Zuerst: Anweisung an JVM, ein int[] array anzulegen:
private static native int[][] initInt2DArray(int size);
JNIEXPORT jobjectArray JNICALLJava_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
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;}
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;}
Inhalt von tmp nach iarr kopieren
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;}
Array an übergeordnetes Objekt-Array result übergeben
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;}
Kontrolle über iarr an JVM geben
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
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
Zugriff auf Static-Feld
Java-Klasse mit statischem Feld si si soll in nativer Methode manipuliert
werdenclass 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
Zugriff auf Static-Feld
JNIEXPORT void JNICALLJava_StaticFieldAccess_accessField(JNIEnv *env, jobject obj){ jfieldID fid; /* store the field ID */ jint si; /* Get a reference to obj’s 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
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"); }}
29
Zugriff auf Instanz-Methode
JNIEXPORT void JNICALLJava_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
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
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
Recommended