Native Java-Schnittstelle
Java Native Interface ( JNI ) ist ein Programmierframework , das es einem in der Java Virtual Machine (JVM) ausgeführten Java -Programm ermöglicht , mit Programmen zu interagieren, die in anderen Sprachen wie C , C++ und Assembly geschrieben wurden .
Zweck und Eigenschaften
JNI wird verwendet, um native Methoden zu schreiben, um Situationen zu überwinden, in denen eine Anwendung nicht vollständig in Java geschrieben werden kann, z. B. wenn die Standardklassenbibliothek keine Unterstützung für plattformabhängige Funktionen bietet.
Es wird auch verwendet, um vorhandene Programme zu modifizieren, die in einer anderen Sprache geschrieben wurden, sodass sie von Java-Anwendungen aus zugänglich sind. Viele der Standard-Java-API-Klassen hängen von der JNI ab, um dem Entwickler und Benutzer Funktionalität bereitzustellen, zum Beispiel Sound- oder Datei-Lese-/Schreib-Funktionalität. Der Entwickler muss sicherstellen, dass die Standard-Java-API eine bestimmte Funktionalität nicht bereitstellt, bevor er auf JNI zurückgreift, da erstere eine sichere und plattformunabhängige Implementierung bietet.
Das JNI- Framework ermöglicht es einer nativen Methode, Java- Objekte auf die gleiche Weise zu verwenden wie Java-Code selbst. Eine native Methode kann Java-Objekte erstellen; und untersuchen und verwenden Sie sie, um ihre Funktion auszuführen. Eine native Methode kann auch Objekte untersuchen und verwenden, die durch in Java geschriebenen Anwendungscode erstellt wurden.
JNI wird oft als „Fluchtventil“ für Entwickler bezeichnet, da es ihnen ermöglicht, ihren Anwendungen Funktionen hinzuzufügen, die die Java- API nicht bieten kann.
Da es – wie bereits gesagt – zur Interaktion mit Code verwendet werden kann, der in anderen Sprachen wie C++ geschrieben wurde, wird es auch für Operationen und Berechnungen mit hoher zeitlicher Komplexität verwendet, da der native Code im Allgemeinen schneller ist als der in einer virtuellen Maschine ausgeführt wird.
Warnungen
JNI ist keineswegs trivial und sollte daher von erfahrenen Programmierern verwendet werden. In jedem Fall hebt die Möglichkeit, Java mit C, C++ oder Assembler zu kommunizieren, alle Beschränkungen dessen auf, was Java-Programme tun können.
Es ist ratsam, die folgenden Punkte zu beachten, wenn Sie die Verwendung von JNI in Betracht ziehen:
- JNI ist keine leicht zu erlernende API .
- Kleine Fehler bei der Verwendung von JNI können die Java Virtual Machine vollständig destabilisieren, und zwar auf eine Weise, die sehr schwer zu reproduzieren und zu beheben ist.
- Nur signierte Anwendungen und Applets können die JNI aufrufen.
- Eine Anwendung, die auf JNI zurückgreift, verliert eine der wichtigsten Funktionen, die Java ihr bietet, ihre Portabilität. (Eine Möglichkeit, dies zu umgehen, besteht darin, für jede Plattform eine separate Implementierung des JNI-Codes zu schreiben und Java das Betriebssystem erkennen zu lassen, um die eine oder andere Implementierung auszuführen, wenn die Zeit gekommen ist.)
- Es gibt keine Garbage Collection auf der JNI-Seite (JNI-Code muss seine Zeiger explizit delokalisieren).
Der automatische Garbage Collector von Java unterscheidet sich etwas von malloc/free in C, da er Objekte verschieben kann, nachdem ihnen der erforderliche Speicher zugewiesen wurde. Es ist daher von entscheidender Bedeutung, dass Zeiger auf Java-Objekte korrekt erhalten und gesperrt werden. Programmierer, die an C gewöhnt sind, verstehen dies oft nicht, was zu ziemlich esoterischen und nicht reproduzierbaren Fehlern führen kann.
Für alle oben genannten Punkte sollte JNI mit Vorsicht verwendet werden und wird von Java-Entwicklern oft gemieden. Beispielsweise kommunizieren die meisten JDBC-Datenbanken direkt mit einem Socket, anstatt vorhandene C-APIs zu verwenden.
Wie das JNI funktioniert
In JNI werden native Funktionen in separaten .c- oder .cpp-Dateien implementiert (C++ bietet eine etwas sauberere Schnittstelle mit JNI). Wenn die virtuelle Maschine die Funktion aufruft, übergibt sie ihr einen Zeiger auf JNIEnv, einen Zeiger auf jobjectund eine beliebige Anzahl von Argumenten, die von der Java-Methode deklariert wurden. Eine JNI-Funktion sollte etwa so aussehen:
JNIEXPORT macht JNICALL Java_ClassName_MethodName ungültig
( JNIEnv * env , jobject obj )
{
//Hier wird die native Methode implementiert
}
Der Zeiger JNIEnv *envist eine Struktur, die die Schnittstelle zur virtuellen Maschine enthält. Es enthält alle notwendigen Funktionen, um mit der JVM (Java Virtual Machine) zu interagieren und mit Java-Objekten zu arbeiten.
Als Beispiele für die Verwendung dieser Schnittstelle können wir die Konvertierung von Vektoren (C-Stil) in/von Java-Vektoren oder native Strings (Zeiger auf Zeichen) von/in Java-Strings (String-Objekte) nennen; Instanziierung von Objekten, Auslösen und Abfangen von Ausnahmen usw.
Im Wesentlichen kann using JNIEnvalles tun, was Java-Code tun kann; ja, mit erheblich erhöhtem Schwierigkeitsgrad.
Beispielsweise konvertiert das folgende Code-Snippet einen Java-String in einen nativen.
//C++-Code
JNIEXPORT void JNICALL Java_ClassName_MethodName
( JNIEnv * env , jobject obj , jstring javaString )
{
// Holen Sie sich den nativen String aus dem Java-String
const char * nativeString = env -> GetStringUTFChars ( javaString , 0 );
// etwas damit machen
/* VERGESSEN SIE DIESE ZEILE NICHT, sie gibt den Platz frei, den der verwendete String belegt
* das hat mit der Art und Weise zu tun, wie Java mit Strings umgeht */
env -> ReleaseStringUTFChars ( javaString , nativeString );
}
//C-Code
JNIEXPORT void JNICALL Java_ClassName_MethodName
( JNIEnv * env , jobject obj , jstring javaString )
{
// Nativen String aus Java-String abrufen
const char * nativeString = ( * env ) -> GetStringUTFChars ( env , javaString , 0 );
// etwas damit machen
/* VERGESSEN SIE DIESE ZEILE NICHT, sie gibt den Platz frei, den der verwendete String belegt
* das hat mit der Art und Weise zu tun, wie Java mit Strings umgeht */
( * env ) -> ReleaseStringUTFChars ( env , javaString , nativeString );
}
Beachten Sie, dass C++ JNI-Code syntaktisch klarer ist als C-Code, da C++ wie Java objektorientierte Methodenaufrufsemantik verwendet,
Dies impliziert, dass der Parameter in C envmit dereferenziert (*env)und explizit an die Methoden von übergeben werden muss (erscheint als Parameter) JNIEnv.
In C++ wird der Parameter envmit dereferenziert env->und implizit als Teil der objektorientierten Methodenaufrufsemantik übergeben (er erscheint nicht als Parameter, da die Methode Teil der env ist, wenn sie als Objekt behandelt wird).
Native Datentypen können Konvertierungen in/von Java-Datentypen unterzogen werden. Bei komplexen Typen wie Objekten, Arrays und Strings muss die native Methode die Daten konvertieren, indem sie explizit Methoden in der JNIEnv(Java Native Interface Environment) aufruft.
Bibliographie
- ShengLiang (1999). Java Native Interface: Programmierhandbuch und Spezifikation . pdf-Version . Addison-Wesley. ISBN 0-201-32577-2 .