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.
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);
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 «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.
NewObject ()
pour l'instanciationCall <type> Method ()
CallStatic <type> Method
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);
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;
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
.
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 */
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);
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.
À 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