Appeler la bibliothèque Java à partir de C avec JNI

En relation avec Utiliser Go pour appeler les bibliothèques Java dont j'ai parlé à TechTalk (session d'étude en interne), C en utilisant JNI Je vais vous expliquer comment appeler Java à partir de.

Java Native Interface(JNI)

JNI est un mécanisme qui vous permet d'utiliser des bibliothèques Java à partir de C et vice versa. La documentation officielle se trouve dans la table des matières de la spécification de l'interface native Java (https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html). Cet article se concentre sur l'appel de méthodes dans le sens de l'utilisation de Java à partir de C.

Chargement et connexion de la JVM

JNI charge la JVM sur l'application native et exécute le code Java dessus. Vous pouvez créer une JVM avec JNI_CreateJavaVM ().

include <jni.h>

JNIEnv *env;
JavaVM *jvm;

 // Options de démarrage JVM, etc.
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_8;
JNI_GetDefaultJavaVMInitArgs(&vm_args);

 // charge la JVM
JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

Je me suis demandé si je devais appeler JNI_DestroyJavaVM () quand j'aurais fini de l'utiliser, [Appeler l'API](https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/ invocation.html # unloading_the_vm) indique: "Le déchargement de VM n'est pas pris en charge."

Certes, si vous essayez de recharger après le déchargement, vous obtiendrez une erreur (JNI_EEXIST). Cela ne veut pas dire que vous devez router la variable jvm. Connectez et utilisez la machine virtuelle Java chargée dans le même processus.

 // Récupère le JavaVM précédemment chargé
JNI_GetCreatedJavaVMs(&jvm, 1, NULL);
 // Joindre pour obtenir JNIEnv
(*jvm)->AttachCurrentThread(jvm, (void **)&env,NULL);

Création d'instance et appel de méthode

Pour l'appel de méthode, spécifiez le nom de la méthode et sa signature (tous les types d'argument et de retour, les détails seront décrits plus tard). Comme Java est un langage qui permet la surcharge de méthodes, le processus à appeler est uniquement déterminé par l'ensemble des types d'arguments et des noms de méthode.

Tout d'abord, obtenez l'ID de méthode qui indique le processus à exécuter par GetMethodID () ou GetStaticMethodID (). Seul le constructeur obtient le nom de méthode spéciale «» avec «GetMethodID ()». D'autres sont spécifiés selon la définition de la méthode. L'exemple suivant est un exemple d'exécution de la méthode statique hello () de la classe auto-créée Test.

 // Tout d'abord, trouvez la classe applicable
jclass clazz = (*env)->FindClass(env, "Test");
 // Récupère l'ID de méthode, méthode statique sans argument dans cet exemple, valeur de retour void
jmethodID id = (*env)->GetStaticMethodID(env, clazz, "hello", "()V");

Ensuite, exécutez la routine suivante qui prend l'ID de méthode obtenu comme argument. <Type> contient une chaîne de caractères en fonction de la valeur de retour telle que Void ou ʻObject. Pour chaque suffixe avec ʻA, transmettez l'argument sous forme de tableau, et pour V, passez-le comme va_list. Aucune marque n'accepte un argument de longueur variable.

Si vous appelez hello () plus tôt, cela ressemblera à ceci.

 // Appelez la méthode en passant la classe, la méthode et les arguments obtenus ci-dessus.
(*env)->CallStaticVoidMethodA(env, clazz, id, NULL);

Gérer les variables Java en C

Lors de l'appel d'une méthode, un objet Java peut être renvoyé en tant qu'argument ou valeur de retour. Chaque type primitif a un type C correspondant et les variables de type de référence (instances et tableaux) sont représentées sous forme de références dans la machine virtuelle Java avec le type jobject. Tous ces types sont membres du complexe jvalue. Le tableau pour chaque taille, etc. peut être trouvé dans Type JNI et structure de données. Il y a.

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

Signature et comment la vérifier

Les signatures sont un moyen de représenter les types Java. Tous les modèles sont indiqués dans le tableau ci-dessous. Les tableaux et les méthodes sont une combinaison d'autres types, et les classes pleinement qualifiées spécifient le délimiteur de package comme «/», comme «Ljava / lang / Object;

Signature de type Type Java
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Classe hautement qualifiée; Classe
[type tableau
(arg-types) ret-type méthode

Vous pouvez voir la signature réelle avec l'option -s de la commande javap. Dans l'exemple ci-dessous, le fichier de classe généré à partir de jnigo / TestClass.java est lu.

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

...

Par exemple, la signature de «TestClass» est «LTestClass;», donc la fonction qui prend «TestClass» comme valeur de retour sans argument est «() LTestClass;». En plus de spécifier directement le fichier de classe, vous pouvez également spécifier FQCN et l'afficher comme javap -s java.lang.String.

Exceptions et descripteurs d'erreur

Les exceptions levées dans JNI ne peuvent pas être connues du côté du code natif sans confirmation active. ʻExceptionCheck () ne peut obtenir que des exceptions avec ou sans exceptions, et ʻExceptionOccurred () peut obtenir des exceptions. ʻExceptionDescribe () `imprime une trace de pile sur stderr.

Si vous utilisez ʻExceptionOccurred () `, vous pouvez écrire l'équivalent de try ~ catch en C, mais pour le moment pour le débogage panique, il est facile d'arrêter le processus comme indiqué ci-dessous.

if ((*env)->ExceptionCheck(env)) {
  (*env)->ExceptionDescribe(env);
  exit(1);
}

Le code d'erreur de JNI lui-même est un échec, sauf s'il s'agit de «JNI_OK». Si l'erreur nécessite une résolution, vous pouvez la gérer avec les numéros d'erreur suivants.

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

Référence globale et garbage collection

Même si la variable de type de référence est utilisée en C, elle sera libérée par le garbage collection après la disparition de la référence côté Java. En contrôlant l'acquisition et la publication de références globales côté code natif, son comportement peut également être contrôlé. Vous pouvez obtenir une référence globale avec NewGlobalRef () et la libérer avec DeleteGlobalRef ().

 // Obtient une référence globale, obj est une variable de type référence
jobject ref = (*env)->NewGlobalRef(env, obj);
 // Libère la référence globale créée
(*env)->DeleteGlobalRef(env, ref);

Épilogue

L'explication se concentre sur la façon d'appeler des méthodes Java à partir de C dans JNI. Les informations ci-dessus sont suffisantes à des fins simples, mais si vous souhaitez les utiliser sérieusement, vous devrez utiliser davantage JNI, comme le comportement du garbage collection et la prise en compte des performances. Il semble que vous puissiez faire quelque chose d'intéressant avec la JVM à partir du code natif.

référence

À propos d'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) Meilleures pratiques pour l'utilisation de l'interface native Java juntaki/jnigo: JNI wrapper for Go

Recommended Posts

Appeler la bibliothèque Java à partir de C avec JNI
Appeler Java depuis JRuby
Appeler une méthode avec le bloc de rappel de Kotlin depuis Java
Appeler la classe scellée de Kotlin depuis Java
Coder Java depuis Emacs avec Eclim
Travailler avec des feuilles de calcul Google à partir de Java
Appelez l'API Java de TensorFlow depuis Scala
Appeler les fonctions du langage C depuis Swift
Crypter avec Java et décrypter avec C #
Appeler l'API GitHub à partir de l'API Socket de Java, partie 2
Intégration API de Java avec Jersey Client
Appel de méthodes Java à partir de JavaScript exécutées en Java
Lier le code Java et C ++ avec SWIG
Appeler java depuis C ++ sur Android NDK
Introduction à Java à partir de 0 Partie 1
Essayez d'appeler Nim depuis Java via JNI
Exécutez du code Java à partir de cpp sur cocos2dx
[Android] Appeler la méthode d'argument par défaut de Kotlin depuis Java
Exécuter Rust depuis Java avec JNA (Java Native Access)
[Java] Réglez l'heure depuis le navigateur avec jsoup
Appel de méthode Java depuis RPG (appel de méthode dans sa propre classe)
Extraction de texte en Java à partir de PDF avec pdfbox-2.0.8
Utilisez Matplotlib depuis Java ou Scala avec Matplotlib4j
Utilisez Fast Mapping Livery MapStruct avec Lombok et Java 11
Comment appeler des fonctions en bloc avec la réflexion Java
Résolution avec Ruby, Perl et Java AtCoder ABC 128 C
[Java] Comment chiffrer avec le chiffrement AES avec une bibliothèque standard
Langage Java du point de vue de Kotlin et C #
Utiliser la bibliothèque C avec Swift en utilisant les modules Clang (modulemap)
[Note] Créez un environnement Java à partir de zéro avec docker
Appeler un programme écrit en Swift depuis Processing (Java)
Générer des modèles de JSON à Swift, PHP, C #, JAVA
Lire la température / l'humidité avec Java de Raspberry Pi 3 & DHT11
Bibliothèque de mesures de couverture Java
Installez java avec Homebrew
Changements de Java 8 à Java 11
Somme de Java_1 à 100
Changer de siège avec Java
Bibliothèque de mappage d'objets JAVA
Installez Java avec Ansible
Évaluer la source Java à partir de Java
Téléchargement confortable avec JAVA
Accédez à API.AI depuis Java
Changer java avec direnv
De Java à Ruby !!
Bibliothèque de cartes bidirectionnelles Java
Téléchargement Java avec Ansible
Traitement des appels du constructeur JAVA
Bibliothèque Java CSV "opencsv"
Raclons avec Java! !!
Construire Java avec Wercker
Bibliothèque d'arborescence Java
Conversion Endian avec JAVA
Trouvez la classe d'adresse et le type d'adresse à partir de l'adresse IP avec Java
[Java] Obtenir le jeu de caractères avec Apathce Tika / Initialiser la chaîne à partir du jeu de caractères [Kotlin]
Implémentez l'interface Java dans la classe JRuby et appelez-la depuis Java
Kotlin post- et pré-incrémentation et surcharge des opérateurs (comparaison avec C, Java, C ++)
[Kotlin] Obtenez le constructeur / la méthode Java de KFunction et appelez-le
A étudié comment appeler des services avec Watson SDK pour Java