Java Language
Native Java-Schnittstelle
Suche…
Parameter
Parameter | Einzelheiten |
---|---|
JNIEnv | Zeiger auf die JNI-Umgebung |
Jobobjekt | Das Objekt, das die nicht static native Methode aufgerufen hat |
jclass | Die Klasse, die die static native Methode aufgerufen hat |
Bemerkungen
Zum Einrichten von JNI sind sowohl ein Java-Compiler als auch ein systemeigener Compiler erforderlich. Je nach IDE und Betriebssystem sind einige Einstellungen erforderlich. Eine Anleitung für Eclipse finden Sie hier . Eine vollständige Anleitung finden Sie hier .
Dies sind die Schritte zum Einrichten der Java-C ++ - Verknüpfung unter Windows:
- Kompilieren Sie die Java-Quelldateien (
.java
) mitjavac
in Klassen (.class
). - Erstellen Sie Header-Dateien (
.h
) aus den Java-Klassen, dienative
Methoden enthalten, mitjavah
. Diese Dateien "weisen" den nativen Code an, für welche Methoden er verantwortlich ist. - Fügen Sie die Header - Dateien (
#include
) in der C ++ Quelldateien (.cpp
) die Umsetzungnative
Methoden. - Kompilieren Sie die C ++ - Quelldateien und erstellen Sie eine Bibliothek (
.dll
). Diese Bibliothek enthält die native Code-Implementierung. - Geben Sie den Bibliothekspfad (
-Djava.library.path
) an und laden Sie ihn in die Java-Quelldatei (System.loadLibrary(...)
).
Callbacks (Aufruf von Java-Methoden aus nativem Code) erfordert die Angabe eines Methodendeskriptors. Wenn der Deskriptor falsch ist, tritt ein Laufzeitfehler auf. Aus diesem Grund ist es hilfreich, die Deskriptoren für uns erstellen zu javap -s
. Dies kann mit javap -s
.
C ++ - Methoden von Java aus aufrufen
Statische und Member-Methoden in Java können als native markiert werden , um anzuzeigen, dass ihre Implementierung in einer gemeinsam genutzten Bibliotheksdatei zu finden ist. Bei der Ausführung einer systemeigenen Methode sucht die JVM nach einer entsprechenden Funktion in geladenen Bibliotheken (siehe Laden von systemeigenen Bibliotheken ), verwendet ein einfaches Namensveränderungsschema, führt die Argumentkonvertierung und das Stack-Setup durch und übergibt die Kontrolle an den nativen Code.
Java-Code
/*** com/example/jni/JNIJava.java **/
package com.example.jni;
public class JNIJava {
static {
System.loadLibrary("libJNI_CPP");
}
// Obviously, native methods may not have a body defined in Java
public native void printString(String name);
public static native double average(int[] nums);
public static void main(final String[] args) {
JNIJava jniJava = new JNIJava();
jniJava.printString("Invoked C++ 'printString' from Java");
double d = average(new int[]{1, 2, 3, 4, 7});
System.out.println("Got result from C++ 'average': " + d);
}
}
C ++ - Code
Header-Dateien, die native Funktionsdeklarationen enthalten, sollten mit dem javah
Tool für javah
generiert werden. Führen Sie den folgenden Befehl im Build-Verzeichnis aus:
javah -o com_example_jni_JNIJava.hpp com.example.jni.JNIJava
... erzeugt die folgende Header-Datei (aus Gründen der Kürze entfernte Kommentare ):
// com_example_jni_JNIJava.hpp
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // The JNI API declarations
#ifndef _Included_com_example_jni_JNIJava
#define _Included_com_example_jni_JNIJava
#ifdef __cplusplus
extern "C" { // This is absolutely required if using a C++ compiler
#endif
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString
(JNIEnv *, jobject, jstring);
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average
(JNIEnv *, jclass, jintArray);
#ifdef __cplusplus
}
#endif
#endif
Hier ist eine Beispielimplementierung:
// com_example_jni_JNIJava.cpp
#include <iostream>
#include "com_example_jni_JNIJava.hpp"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString(JNIEnv *env, jobject jthis, jstring string) {
const char *stringInC = env->GetStringUTFChars(string, NULL);
if (NULL == stringInC)
return;
cout << stringInC << endl;
env->ReleaseStringUTFChars(string, stringInC);
}
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average(JNIEnv *env, jclass jthis, jintArray intArray) {
jint *intArrayInC = env->GetIntArrayElements(intArray, NULL);
if (NULL == intArrayInC)
return -1;
jsize length = env->GetArrayLength(intArray);
int sum = 0;
for (int i = 0; i < length; i++) {
sum += intArrayInC[i];
}
env->ReleaseIntArrayElements(intArray, intArrayInC, 0);
return (double) sum / length;
}
Ausgabe
Wenn Sie die obige Beispielklasse ausführen, erhalten Sie die folgende Ausgabe:
C ++ 'printString' von Java aufgerufen
Ergebnis aus C ++ "Durchschnitt": 3.4
Java-Methoden von C ++ aus aufrufen (Callback)
Das Aufrufen einer Java-Methode aus nativem Code erfolgt in zwei Schritten:
- einen Methodenzeiger mit der
GetMethodID
JNI-Funktion erhalten, wobei der Methodenname und der Deskriptor verwendet werden; - Rufen Sie eine der hier aufgeführten
Call*Method
Funktionen auf.
Java-Code
/*** com.example.jni.JNIJavaCallback.java ***/
package com.example.jni;
public class JNIJavaCallback {
static {
System.loadLibrary("libJNI_CPP");
}
public static void main(String[] args) {
new JNIJavaCallback().callback();
}
public native void callback();
public static void printNum(int i) {
System.out.println("Got int from C++: " + i);
}
public void printFloat(float i) {
System.out.println("Got float from C++: " + i);
}
}
C ++ - Code
// com_example_jni_JNICppCallback.cpp
#include <iostream>
#include "com_example_jni_JNIJavaCallback.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJavaCallback_callback(JNIEnv *env, jobject jthis) {
jclass thisClass = env->GetObjectClass(jthis);
jmethodID printFloat = env->GetMethodID(thisClass, "printFloat", "(F)V");
if (NULL == printFloat)
return;
env->CallVoidMethod(jthis, printFloat, 5.221);
jmethodID staticPrintInt = env->GetStaticMethodID(thisClass, "printNum", "(I)V");
if (NULL == staticPrintInt)
return;
env->CallVoidMethod(jthis, staticPrintInt, 17);
}
Ausgabe
Erhielt Float aus C ++: 5.221
Bekam Int von C ++: 17
Den Deskriptor bekommen
Deskriptoren (oder interne Typunterschriften ) werden mit dem javap- Programm der kompilierten .class
Datei abgerufen. Hier ist die Ausgabe von javap -p -s com.example.jni.JNIJavaCallback
:
Compiled from "JNIJavaCallback.java"
public class com.example.jni.JNIJavaCallback {
static {};
descriptor: ()V
public com.example.jni.JNIJavaCallback();
descriptor: ()V
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
public native void callback();
descriptor: ()V
public static void printNum(int);
descriptor: (I)V // <---- Needed
public void printFloat(float);
descriptor: (F)V // <---- Needed
}
Laden nativer Bibliotheken
Das übliche Idiom zum Laden von gemeinsam genutzten Bibliotheksdateien in Java lautet wie folgt:
public class ClassWithNativeMethods {
static {
System.loadLibrary("Example");
}
public native void someNativeMethod(String arg);
...
Aufrufe von System.loadLibrary
sind fast immer statisch, sodass sie beim Laden der Klasse auftreten. System.loadLibrary
wird sichergestellt, dass keine native Methode ausgeführt werden kann, bevor die gemeinsam genutzte Bibliothek geladen wurde. Folgendes ist jedoch möglich:
public class ClassWithNativeMethods {
// Call this before using any native method
public static void prepareNativeMethods() {
System.loadLibrary("Example");
}
...
Dies ermöglicht, das Laden der gemeinsam genutzten Bibliothek zu verschieben, bis dies erforderlich ist, erfordert jedoch besondere Vorsicht, um java.lang.UnsatisfiedLinkError
s zu vermeiden.
Zieldatei-Lookup
java.library.path
gemeinsam genutzten Bibliotheksdateien wird in den Pfaden gesucht, die in der java.library.path
definiert sind. java.library.path
können mit dem Argument -Djava.library.path=
JVM zur Laufzeit überschrieben werden:
java -Djava.library.path=path/to/lib/:path/to/other/lib MainClassWithNativeMethods
Achten Sie auf Systempfadtrennzeichen: zum Beispiel für Windows ;
anstelle von :
Beachten Sie, dass System.loadLibrary
Bibliotheksdateinamen System.loadLibrary
auflöst: Der obige Code-Snippet erwartet unter Linux eine Datei namens libExample.so
und Example.dll
Windows Example.dll
.
Eine Alternative zu System.loadLibrary
ist System.load(String)
, die den vollständigen Pfad zu einer gemeinsam genutzten Bibliotheksdatei verwendet und die Suche nach java.library.path
umgeht:
public class ClassWithNativeMethods {
static {
System.load("/path/to/lib/libExample.so");
}
...