Im Zusammenhang mit Verwenden von Go zum Aufrufen von Java-Bibliotheken, über das ich bei TechTalk (interne Studiensitzung) gesprochen habe, C mit JNI Ich werde erklären, wie man Java aufruft.
Java Native Interface(JNI)
JNI ist ein Mechanismus, mit dem Sie Java-Bibliotheken von C und umgekehrt verwenden können. Die offizielle Dokumentation finden Sie im Inhaltsverzeichnis der Java Native Interface-Spezifikation (https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html). Dieser Artikel konzentriert sich auf das Aufrufen von Methoden in Richtung der Verwendung von Java aus C.
JNI lädt die JVM in die native App und führt den Java-Code darauf aus.
Sie können eine JVM mit JNI_CreateJavaVM ()
erstellen.
include <jni.h>
JNIEnv *env;
JavaVM *jvm;
// JVM-Startoptionen usw.
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_8;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
// JVM laden
JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
Ich fragte mich, ob ich "JNI_DestroyJavaVM ()" aufrufen sollte, wenn ich es nicht mehr verwendet habe, Call API. invocation.html # unloading_the_vm) sagt: "Das Entladen von VMs wird nicht unterstützt."
Wenn Sie versuchen, nach dem Entladen neu zu laden, wird eine Fehlermeldung angezeigt (JNI_EEXIST
). Das bedeutet nicht, dass Sie die Variable "jvm" weiterleiten müssen. Fügen Sie die im selben Prozess geladene JVM hinzu und verwenden Sie sie.
// Holen Sie sich die zuvor geladene JavaVM
JNI_GetCreatedJavaVMs(&jvm, 1, NULL);
// Anhängen, um JNIEnv zu erhalten
(*jvm)->AttachCurrentThread(jvm, (void **)&env,NULL);
Geben Sie für den Methodenaufruf den Methodennamen und seine Signatur an (alle Argument- und Rückgabetypen, Details werden später beschrieben). Da Java eine Sprache ist, die das Überladen von Methoden ermöglicht, wird der aufzurufende Prozess eindeutig durch die Menge der Argumenttypen und Methodennamen bestimmt.
Rufen Sie zunächst die Methoden-ID ab, die den von GetMethodID () oder GetStaticMethodID () auszuführenden Prozess angibt. Nur der Konstruktor erhält mit GetMethodID () den speziellen Methodennamen
// Finde zuerst die zutreffende Klasse
jclass clazz = (*env)->FindClass(env, "Test");
// Hole die Methoden-ID, statische Methode ohne Argument in diesem Beispiel, Rückgabewert void
jmethodID id = (*env)->GetStaticMethodID(env, clazz, "hello", "()V");
Führen Sie als Nächstes die folgende Routine aus, die die erhaltene Methoden-ID als Argument verwendet. <Typ>
enthält eine Zeichenfolge entsprechend dem Rückgabewert wie Void
oder Object
. Übergeben Sie für jedes Suffix mit "A" das Argument als Array und für "V" als "va_list". Keine Markierung akzeptiert ein Argument variabler Länge.
NewObject ()
zur InstanziierungCall <type> Method ()
Wenn Sie früher "Hallo ()" aufrufen, sieht es so aus.
// Rufe die Methode auf, indem du die oben erhaltenen Klassen, Methoden und Argumente übergibst.
(*env)->CallStaticVoidMethodA(env, clazz, id, NULL);
Beim Aufrufen einer Methode kann ein Java-Objekt als Argument oder Rückgabewert zurückgegeben werden. Jeder primitive Typ hat einen entsprechenden C-Typ, und Referenztypvariablen (Instanzen und Arrays) werden in der JVM mit dem Typ jobject als Referenzen dargestellt. Alle diese Typen sind Mitglieder des jvalue-Komplexes. Die Tabelle für jede Größe usw. finden Sie unter JNI-Typ und Datenstruktur. Es gibt.
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
Signaturen sind eine Möglichkeit, Java-Typen darzustellen. Alle Muster sind in der folgenden Tabelle aufgeführt. Arrays und Methoden sind eine Kombination anderer Typen, und vollständig qualifizierte Klassen geben das Pakettrennzeichen als "/" an, z. B. "Ljava / lang / Object;".
Typ Signatur | Java-Typ |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
Hochqualifizierte Klasse; | Klasse |
[Typ | Array |
(arg-types) ret-type | Methode |
Sie können die tatsächliche Signatur mit der Option -s
des Befehls javap
anzeigen.
Im folgenden Beispiel wird die aus jnigo / TestClass.java generierte Klassendatei gelesen.
$ javap -s TestClass.class
Compiled from "TestClass.java"
public class TestClass {
public boolean vboolean;
descriptor: Z
public byte vbyte;
descriptor: B
...
public static double smvdouble();
descriptor: ()D
public static TestClass smvclass();
descriptor: ()LTestClass;
...
Beispielsweise lautet die Signatur von "TestClass" "LTestClass", sodass die Funktion, die "TestClass" als Rückgabewert ohne Argumente verwendet, "() LTestClass" ist. Zusätzlich zur direkten Angabe der Klassendatei können Sie auch FQCN angeben und wie "javap -s java.lang.String" anzeigen.
In JNI ausgelöste Ausnahmen können auf der nativen Codeseite ohne aktive Bestätigung nicht bekannt sein.
ExceptionCheck () kann nur Ausnahmen mit oder ohne Ausnahmen abrufen, und ExceptionOccurred () kann Ausnahmen abrufen. ExceptionDescribe ()
druckt einen Stack-Trace an stderr.
Wenn Sie "ExceptionOccurred ()" verwenden, können Sie das Äquivalent von try ~ catch in C schreiben. Für das Panik-Debugging ist es jedoch vorerst einfach, den Vorgang wie unten gezeigt zu stoppen.
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
exit(1);
}
Der Fehlercode von JNI selbst ist ein Fehler, es sei denn, es handelt sich um "JNI_OK". Wenn der Fehler behoben werden muss, können Sie ihn mit den folgenden Fehlernummern behandeln.
/*
* possible return values for JNI functions.
*/
define JNI_OK 0 /* success */
define JNI_ERR (-1) /* unknown error */
define JNI_EDETACHED (-2) /* thread detached from the VM */
define JNI_EVERSION (-3) /* JNI version error */
define JNI_ENOMEM (-4) /* not enough memory */
define JNI_EEXIST (-5) /* VM already created */
define JNI_EINVAL (-6) /* invalid arguments */
Selbst wenn die Referenztypvariable in C verwendet wird, wird sie von der Garbage Collection freigegeben, nachdem die Referenz auf der Java-Seite verschwunden ist. Durch die Steuerung der Erfassung und Freigabe globaler Referenzen auf der nativen Codeseite kann auch deren Verhalten gesteuert werden. Sie können eine globale Referenz mit "NewGlobalRef ()" abrufen und mit "DeleteGlobalRef ()" freigeben.
// Globale Referenz abrufen, obj ist eine Referenztypvariable
jobject ref = (*env)->NewGlobalRef(env, obj);
// Die erstellte globale Referenz freigeben
(*env)->DeleteGlobalRef(env, ref);
Die Erklärung konzentriert sich darauf, wie Java-Methoden von C in JNI aufgerufen werden. Die oben genannten Informationen sind für einfache Zwecke ausreichend. Wenn Sie sie jedoch ernsthaft verwenden möchten, müssen Sie JNI stärker verwenden, z. B. das Verhalten der Speicherbereinigung und die Berücksichtigung der Leistung. Es sieht so aus, als könnten Sie mit der JVM aus nativem Code etwas Interessantes machen.
Informationen zum IBM Knowledge Center - Java Native Interface (JNI)](https://www.ibm.com/support/knowledgecenter/ja/SSYKE2_8.0.0/com.ibm.java.lnx.80.doc/diag/understanding/ jni.html) Best Practices für die Verwendung der Java Native Interface juntaki/jnigo: JNI wrapper for Go
Recommended Posts