Rufen Sie die Java-Bibliothek von C mit JNI auf

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.

Laden und Anhängen der JVM

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);

Instanzerstellung und Methodenaufruf

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 . Andere werden gemäß der Methodendefinition angegeben. Das folgende Beispiel ist ein Beispiel für die Ausführung der statischen Methode "hello ()" der selbst erstellten Klasse "Test".

 // 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.

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);

Behandeln Sie Java-Variablen in C.

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;

Unterschrift und wie man sie überprüft

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.

Ausnahmen und Fehlerbehandlungen

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 */

Globale Referenz und Speicherbereinigung

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);

Nachwort

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.

Referenz

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

Rufen Sie die Java-Bibliothek von C mit JNI auf
Rufen Sie Java von JRuby aus auf
Rufen Sie eine Methode mit Kotlins Rückrufblock von Java aus auf
Rufen Sie Kotlins versiegelte Klasse von Java aus an
Code Java von Emacs mit Eclim
Arbeiten Sie mit Google-Tabellen aus Java
Rufen Sie die Java-API von TensorFlow von Scala aus auf
Rufen Sie C-Sprachfunktionen von Swift aus auf
Mit Java verschlüsseln und mit C # entschlüsseln
Rufen Sie die GitHub-API über Javas Socket-API Teil2 auf
API-Integration von Java mit Jersey Client
Rufen Sie Java-Methoden aus JavaScript auf, das in Java ausgeführt wird
Verknüpfen Sie Java- und C ++ - Code mit SWIG
Rufen Sie Java von C ++ auf Android NDK auf
Einführung in Java ab 0 Teil 1
Versuchen Sie, Nim von Java über JNI aufzurufen
Führen Sie Java-Code von cpp auf cocos2dx aus
[Android] Rufen Sie Kotlins Standardargumentmethode von Java aus auf
Führen Sie Rust von Java mit JNA (Java Native Access) aus.
[Java] Stellen Sie die Zeit im Browser mit jsoup ein
Java-Methodenaufruf von RPG (Methodenaufruf in eigener Klasse)
Textextraktion in Java aus PDF mit pdfbox-2.0.8
Verwenden Sie Matplotlib aus Java oder Scala mit Matplotlib4j
Verwenden Sie die schnelle Mapping-Bibliothek MapStruct mit Lombok und Java 11
Aufrufen von Funktionen in großen Mengen mit Java Reflection
Lösen mit Ruby, Perl und Java AtCoder ABC 128 C.
[Java] Verschlüsselung mit AES-Verschlüsselung mit Standardbibliothek
Java-Sprache aus der Sicht von Kotlin und C #
Verwenden Sie die C-Bibliothek mit Swift unter Verwendung von Clang-Modulen (Modulkarte).
[Hinweis] Erstellen Sie mit Docker eine Java-Umgebung von Grund auf neu
Rufen Sie ein in Swift geschriebenes Programm von Processing (Java) auf.
Generieren Sie Modelle von JSON zu Swift, PHP, C #, JAVA
Lesen Sie Temperatur / Luftfeuchtigkeit von Raspberry Pi 3 & DHT11 mit Java ab
Bibliothek zur Messung der Java-Abdeckung
Installieren Sie Java mit Homebrew
Änderungen von Java 8 zu Java 11
Summe von Java_1 bis 100
Wechseln Sie die Plätze mit Java
JAVA-Objektzuordnungsbibliothek
Installieren Sie Java mit Ansible
Eval Java-Quelle von Java
Bequemer Download mit JAVA
Greifen Sie über Java auf API.AI zu
Schalten Sie Java mit direnv
Von Java zu Ruby !!
Bidirektionale Java-Kartenbibliothek
Java-Download mit Ansible
JAVA-Konstruktoraufrufverarbeitung
Java CSV Bibliothek "opencsv"
Lass uns mit Java kratzen! !!
Erstellen Sie Java mit Wercker
Java-Baumstrukturbibliothek
Endian-Konvertierung mit JAVA
Suchen Sie die Adressklasse und den Adresstyp aus der IP-Adresse mit Java
[Java] Zeichensatz mit Apathce Tika abrufen / String von Zeichensatz initialisieren [Kotlin]
Implementieren Sie die Java-Schnittstelle in der JRuby-Klasse und rufen Sie sie von Java aus auf
Kotlin Post- und Pre-Inkrement und Operatorüberladung (Vergleich mit C, Java, C ++)
[Kotlin] Holen Sie sich Java Constructor / Method von KFunction und rufen Sie es auf
Untersucht, wie Dienste mit Watson SDK für Java aufgerufen werden