La communication entre java et C consiste à pouvoir appeler, à partir
d'un code java, un sous-programme écrit en C, ou à pouvoir utiliser une
classe java (champ et méthode) à partir d'un programme écrit en C.
Bien évidemment il faut aussi permettre le passage de paramètres
c'est à dire utiliser ou créer, en langage C des éléments java (types de
base et objets).
Ce problème d'interface a deux volets; d'une part, du point de vue compilation, il faut disposer en C des équivalents des types de base et du type 'String' de java; d'autre part, du point de vue exécution il faut soit 'adjoindre' à la machine virtuelle java le code machine issu du C (si l'échange est initié coté java) soit lancer une machine virtuelle java à partir d'un processus issu de C (si l'échange est initié coté C).
L'utilisation de sous-programmes écrits en C, à partir d'un programme écrit en Java demande d'abord de déclarer en Java une classe et une méthode, puis d'appeler cette méthode. Ensuite il faut savoir comment programmer la fonction C pour utiliser les paramètres qu'elle reçoit de l'appel en Java. En résumé il faut une réponse aux questions suivantes.
L'utilisation d'objets écrits en Java, à partir d'un programme écrit en C demande de connaître la représentation en mémoire des objets créés par la machine virtuelle Java. Ensuite, au moment de l'exécution, il faut activer ces objets Java, c'est à dire lancer une machine virtuelle, y charger une classe, puis un objet, afin d'accéder à un champ ou une méthode.
La réponse à ces questions se trouve dans l'environnement JNI qui comprend évidemment une partie Java (commandes javah, javap) et une partie C (fichier jni.h ...).
Ce paragraphe présente l'utilisation, en Java, d'une méthode simple dont
le code est écrit en C (nous parlons de fonction C), afin d'illustrer le
passage du nom en java au nom en C (qui reflète nom de classe et nom de
méthode) et l'adjonction à la machine virtuelle java du code C.
On utilise l'environnement C++.
Pour satisfaire les exigences des deux langages, on commence par écrire, et compiler une classe Java. Puis un fichier d'entête contenant la déclaration de la fonction C est générée automatiquement par l'utilitaire javah. On écrit ensuite le code de la fonction C, on le compile et on l'inclut dans une bibliothèque partagée. Enfin la machine virtuelle java est lancée, de façon qu'elle ait connaissance de cette bibliothèque.
Nous appelons Coucou.java le fichier contenant la classe Coucou et sa méthode affiche() qui doit va implantée en C; nous appelons coucou.h le fichier d'en-tête issu de cette classe, et coucou.C le fichier C++ qui définit le code de la fonction C associée à la méthode java affiche().
Voyons ces étapes en détail.
/* Coucou.java : première utilisation de JNI */ class Coucou { private native void affiche(); public static void main(String args[]) { new Coucou().affiche(); } static { System.loadLibrary("coucou"); } }et compiler la classe java:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Coucou */ #ifndef _Included_Coucou #define _Included_Coucou #ifdef __cplusplus extern "C" { #endif /* * Class: Coucou * Method: affiche * Signature: ()V */ JNIEXPORT void JNICALL Java_Coucou_affiche (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
Remarquer le nom 'Java_Coucou_affiche' composé avec le nom de la classe et celui de la méthode; remarquer les paramètres relatifs à l'environnement, de type JNIEnv* et jobject, qui ne sont pas utilisés dans cet exemple simple.
Après avoir copié sur le fichier d'entête crée par javah le
prototype associé à la méthode affiche(), on code le source,
sans oublier d'inclure "coucou.h" et <jni.h>
/* coucou.C */ #include <jni.h> #include "coucou.h" #include <iostream.h> JNIEXPORT void JNICALL Java_Coucou_affiche (JNIEnv *, jobject) { cout<<"Depuis code C++ \n ... j'affiche ...\n COUCOU"<<endl; }Dès qu'on a le nom du répertoire contenant le fichier jni.h; (dans la distribution des fichiers du JDK) on peut compiler.
La commande dépend du système d'exploitation et de l'environnement
de programmation C utilisé. Nous créons, dans le sous-répertoire
Pour le système AIX et l'environnement xlC, la commande est:
makeC++SharedLib -p 1 -o bib/libcoucou.so coucou.o
Pour le système linux et l'environnement GNU, la commande est:
g++ -shared -o bib/libcoucou.so coucou.o
L'exécution se fait après avoir rendu accessible à la machine virtuelle
java le code créé; le chargement de la classe Coucou est standard.
Pour le système AIX, les commandes sont:
export LIBPATH=~/app/java/jni/bib
java Coucou
Pour le système linux, les commandes sont:
export LD_LIBRARY_PATH=~/app/java/jni/bib
java Coucou
Cet exemple montre comment, dans une fonction C, utiliser un objet
String de java, et comment en construire un, qui est la valeur
de retour d'une fonction.
Il faut écrire en C le code de
la méthode 'String saisirLigne(String msg);'
(prototype Java) qui affiche le texte
msg, puis saisit une ligne au clavier, et renvoie les caractères
saisis sous forme d'objet Java 'String'.
Nous notons SaisirChaine.java le fichier java, ainsi que
saisirChaine.h saisirChaine.C les fichiers utilisés en
C++. La bibliothèque partagée s'appelle libsaisir.so.
Le code Java, écrit ci-après, est simple.
/* SaisirChaine.java passage de chaînes entre Java et C++ Résumé des opérations (répertoire courant ~/app/java/jni) Ecrire et compiler classe java: javac SaisirChaine.java Générer le fichier d'entête : javah -o saisirChaine.h SaisirChaine Ecrire le source en C++, saisirChaine.C, et le compiler xlC -c -I/usr/java130/include saisirChaine.C Créer bibliothèque: makeC++SharedLib -p 1 -o bib/libsaisir.so saisirChaine.o Exécuter: export LIBPATH=~/app/java/jni/bib java SaisirChaine Chaine */ class SaisirChaine { private native String saisirLigne(String msg); public static void main(String args[]) { SaisirChaine p = new SaisirChaine(); String ligne = p.saisirLigne("Ligne: "); System.out.println("A été tapé: " + ligne); } static { System.loadLibrary("saisir"); } }
Dans le fichier d'entête généré par javah
Java_SaisirChaine_saisirLigne est le nom de la fonction, et le type
jstring (défini dans jni.h) correspond à la classe String de
Java.
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class SaisirChaine */ #ifndef _Included_SaisirChaine #define _Included_SaisirChaine #ifdef __cplusplus extern "C" { #endif /* * Class: SaisirChaine * Method: saisirLigne * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_SaisirChaine_saisirLigne (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
Voyons dans le source en C++, comment est utilisé le paramètre msg_java de type 'jstring', qui représente l'objet de la classe String' de Java. Nous appelons descripteur de chaîne ce type jstring, à partir duquel on extrait, par la fonction GetStringUTFChars, la suite d'octets, représentant la chaîne de caractères en C(avec le codage UTF8), de type 'char *'.
On distingue évidemment, le type jstring descripteur d'une
chaîne Java, et le type char* qui représente une chaîne en C.
Après utilisation, les zones mémoire (msg_data, et msg) utilisées
dans la programmation C doivent être libérées par la fonction
Nous avons également, dans le source ci-dessous, l'utilisation de la fonction NewStringUTF pour créer un objet String de Java (type jstring en C), à partir d'une chaîne en C (cf tableau ligne).
/* saisirChaine.C chaines entre java et C++ */ #include <jni.h> #include "saisirChaine.h" #include <iostream.h> JNIEXPORT jstring JNICALL Java_SaisirChaine_saisirLigne( JNIEnv *penv, jobject obj, jstring msg_java) { char ligne[1024+1]; const char * msg = penv->GetStringUTFChars(msg_java,0); cout << "\nSaisie depuis C++\n "<<msg; penv->ReleaseStringUTFChars(msg_java,msg); cin.getline(ligne,sizeof(ligne)); return penv->NewStringUTF(ligne); }
Les tableaux sont des objets particuliers de java, qui ne correspondent
pas à des classes. Cet exemple montre comment utiliser en C un paramètre
de type tableau. L'information associée à un paramètre tableau en Java
est l'adresse d'une zone mémoire où figurent tout ce qui est nécessaire
pour l'accès à un élément, comme par exemple, la taille du tableau (Java
détecte la validité ou non de l'indice fournit), l'adresse de
l'élément d'indice 0, ...
Nous appelons descripteur de tableau cette information.
Nous notons UtilTab.java le fichier contenant la classe Java, et utilTab.h, UtilTab.C les fichiers contenant le code C++; la bibliothèque est notée libutil.so.
Dans le code java ci-dessous, la méthode sommeTab prend un tableau d'entiers en paramètre et renvoie la somme de ces éléments.
/* UtilTab.java une tableau entre java et C++ Ecrire le code de la classe java Compiler la classe java : javac UtilTab.java Créer le fichier d'entête : javah -o utilTab.h UtilTab Ecrire le source en C++ fichier utilTab.C xlC -c -I/usr/java130/include utilTab.C Créer une bibliothèque : makeC++SharedLib -p 1 -o libutil.so utilTab.o Exécuter : java UtilTab Remarques ========= avec g++ l'obtention de bibliothèque partagée est: g++ -c -I/usr/java130/include utilTab.C # compilation g++ -shared -o libutil.so utilTab.o # bibliothèque on peut mettre plusieurs modules objets dans une seule bibliothèque */ class UtilTab { private native int sommeTab(int t[]); public static void main(String args[]) { UtilTab p = new UtilTab(); int t[] = new int[] { 5,15,7,13 }; System.out.println("Somme calculée: " + p.sommeTab(t)); } static { System.loadLibrary("util"); } }
Le fichier d'entête généré par javah figure ci-après. Y voir le nom de la méthode, Java_UtilTab_sommeTab et le type jintArray du descipteur de tableau d'entiers en Java.
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class UtilTab */ #ifndef _Included_UtilTab #define _Included_UtilTab #ifdef __cplusplus extern "C" { #endif /* * Class: UtilTab * Method: sommeTab * Signature: ([I)I */ JNIEXPORT jint JNICALL Java_UtilTab_sommeTab (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif
Nous voyons dans le code C++, l'utilisation de la fonction GetArrayLength qui donne la taille du tableau, et l'utilisation de GetIntArrayElements pour obtenir l'adresse du premier octet , type jint* de la version C++ du tableau. Ces informations sont obtenues à partir du parametre t_java de type jintArray, qui est le descripteur du tableau d'entiers.
Après utilisation, les zones mémoire (t_java, et t) utilisées dans la programmation C doivent être libérées par la fonction ReleaseIntArrayElements. En effet la gestion de ces zones, allouées par les routines de JNI, doit être cohérente avec la gestion mémoire de la machine virtuelle Java.
Ainsi, comme pour les chaînes, il faut distinguer:
le type jintArray qui représente un descripteur
du type jint* qui représente un tableau en C.
Evidemment le type
jintArray porte plus d'informations que le type jint*,
puisque le premier nous permet d'avoir la taille du tableau.
Signalons aussi que pour chaque type basique (boolean, char ... double)
il existe:
un type pour le descripteur:
jbooleanArray jcharArray ... jdoubleArray
des fonctions associées:
GetBooleanArrayElements ... GetDoubleArrayElements
un type pour le tableau en C:
jboolean* jchar* ... jdouble*
Enfin, pour traiter un objet de classe quelconque, il existe le
type jobjectArray qui représente le descripteur d'un tableau
d'objets en Java, la fonction GetObjectArrayElements, et le
type jobject* qui représente un tableau.
/* utilTab.C */ #include <jni.h> #include "utilTab.h" #include <iostream.h> JNIEXPORT jint JNICALL Java_UtilTab_sommeTab (JNIEnv * penv, jobject obj, jintArray t_java) { jsize lg = penv->GetArrayLength(t_java); int somme,i; jint *t = penv -> GetIntArrayElements(t_java,0); for( i=0,somme=0; i<lg; i++) cout<<t[i]<<" ",somme += t[i]; penv->ReleaseIntArrayElements(t_java,t,0); cout <<" calcul effectué dans une fonction de utilTab.C"<<endl; return somme; }
A partir d'un source C++ on peut atteindre les champs d'un objet et les champs statiques d'une classe java, soit pour consultation soit pour modification. On peut, de façon analogue, appeler une méthode d'un objet ou une méthode statique d'une classe.
Pour cela il faut à partir de l'objet et du nom de champ concerné,
obtenir, son "identification en C", qui permettra, ensuite de
consulter ou modifier le champ, soit directement pour un type basique,
soit par l'intermédiaire du descripteur pour une chaîne ou un tableau.
Pour activer une méthode, il faut, de façon analogue, avoir son
"identification en C", à partir de l'objet et du nom de la méthode;
cependant, à cause de la surcharge possible du nom, il faut préciser de
plus la
signature de la méthode recherchee.
La signature d'une méthode tient compte des types de chaque paramètre et
de la valeur renvoyée. Par exemple la signature de la méthode:
void appelCpp(double)
est codée: (D)V
Le codage de la signature obéit à quelques conventions (voir en annexe):
Dans l'exemple ci-dessous un champ 'double' et un champ 'tableau de double' sont modifiés.
La commande javap permet de connaître les
signatures des champs et méthodes d'une classe.
Voir ci-dessous le
codage de la signature de chaque champ de la classe UtilObj,
obtenu par la commande
javap -s -p UtilObj
class UtilObj extends java.lang.Object | |
signature | codage |
---|---|
double d_java; | D |
double td_java[]; | [D |
static int i_java_stq; | I |
UtilObj(); | ()V |
private native void appelCpp(double); | (D)V |
static void aff(); | ()V |
void aff(java.lang.String); | (Ljava/lang/String;)V |
public static void main(java.lang.String[]); | ([Ljava/lang/String;)V |
Dans la classe java ci-après, les champs d_java et td_java sont modifiés
par la méthode appelCpp; remarquons que le tableau td_java est
créé avant d'être modifié par appelCpp.
Nous avons aussi deux méthodes aff, avec des protoypes différents;
elles sont appelées par appelCpp pour montrer comment les distinguer
grace à leur signature.
L'une des méthodes est statique (méthode de classe) afin de montrer
comment la distinction se fait, dans la programmation C, entre l'utilisation
d'un élément (champ ou méthode) statique et celle d'un élément associé
à un objet.
/* UtilObj.java utilisation de champs à partir de code C++ */ class UtilObj { double d_java; double td_java[]=new double[3]; static int i_java_stq; /* modifie d_java par résultat d'un calcul basé sur d */ private native void appelCpp(double d); static void aff() { i_java_stq++; System.out.println("Nb.appels à 'static void aff()' :"+i_java_stq); } void aff(String msg) { System.out.print(msg+d_java+" ["); for(int i=0;i<td_java.length;i++)System.out.print(" "+td_java[i]); System.out.println("]"); } public static void main(String args[]) { UtilObj u = new UtilObj(); u.d_java=-3.14; u.appelCpp(3.14); u.appelCpp(2.718); } static { System.loadLibrary("util"); } }
Le fichier utiliObj.C ci-après, contient le code de la fonction
Java_UtilObj_appelCpp qui définit la méthode appelCpp de la
classe UtilObj.
Le premier groupe d'instructions modife le champ d_java de type double; la variable i_d_java est l'"identification en C" (type jfieldID qui permet d'utiliser ce champ; sa modification, qui n'est pas une affectation directe, se fait par la fonction SetDoubleField. On voit également l'utilisation de GetDoubleField pour connaître la valeur du champ.
Ensuite on modifie le contenu du tableau td_java, après avoir
obtenu son "identification en C" dans la variable
_td_java. Pour cela, on recupère d'abord un "desripteur"
du tableau dans la variable td_java de type
jdoubleArray
Puis, comme dans l'exemple précédent d'utilisation de tableau, on
accède à la longueur (variable lg) et à l'adresse du premier
octet (variable td).
/* utilObj.C */ #include <jni.h> #include "utilObj.h" #include <iostream.h> /* Calcul à partir de d et résultat dans le champ 'double d_java' */ JNIEXPORT void JNICALL Java_UtilObj_appelCpp (JNIEnv *penv, jobject obj, jdouble d) { int i; jclass i_classe= penv->GetObjectClass(obj); // Modification d'un champ de type de base jfieldID i_d_java=penv->GetFieldID( i_classe,"d_java","D"); if(i_d_java==0) return; cout <<"Dans source C++, récupéré: "<<penv->GetDoubleField(obj,i_d_java)<<endl; penv->SetDoubleField(obj,i_d_java,2*d); cout <<" champ modifié: "<<penv->GetDoubleField(obj,i_d_java)<<endl; // Modification d'un tableau de type de base jfieldID i_td_java=penv->GetFieldID( i_classe,"td_java","[D"); if(i_td_java==0) return; jdoubleArray td_java=jdoubleArray(penv->GetObjectField(obj,i_td_java)); jsize lg= penv->GetArrayLength(td_java); cout<<" nb.éléments: "<<lg<<endl;; jdouble *td = penv -> GetDoubleArrayElements(td_java,0); for(i=0; i<lg; i++) td[i]=d; penv->ReleaseDoubleArrayElements(td_java,td,0); // Utilisation d'une méthode de l'objet obj jmethodID i_methode=penv->GetMethodID(i_classe,"aff","(Ljava/lang/String;)V"); if(i_methode==0) return; jstring msg= penv->NewStringUTF(" utilObj.C appelle méthode java "); penv->CallVoidMethod(obj,i_methode, msg); penv->ReleaseStringUTFChars(msg,NULL); // Appel d'une méthode statique de la classe UtilObj (ou méthode de classe) i_methode=penv->GetStaticMethodID(i_classe,"aff","()V"); if(i_methode==0) return; penv->CallStaticVoidMethod(i_classe,i_methode, msg); }
On peut avoir aussi la possibilité qu'un processus issu d'un programme en C
utilise des classes java. Pour cela le processus doit activer
la machine virtuelle java, puis utiliser, comme nous
avons vu ci-dessus, des méthodes de classes Java .
Le lancement de la machine virtuelle java est nouveau.
Pour illustrer le lancement d'un M.V.J, nous utilisons une petite
classe, dont le source est ci-dessous; puis nous voyons la
programmation en C++ du lancement.
public class AppelMV { public static void main(String[] mots) { System.out.println("Coucou !!! " + mots[0]); } }
/* appelMV.cc xlC -c -I/usr/java130/include appelMV.cc http://www.nawouak.net/?doc=bpp_library+lang=fr Utilisation windows http://penserenjava.free.fr/pens/indexMain_18&0.htm #linux export RJH=/usr/java/j2sdk1.4.1_06/ #compilation export RJI=${RJH}include g++ -Wno-deprecated -I$RJI -I$RJI/linux appelMV.cc # liaison # répertoire bibliothèque java (3 bib: libjava libverify libjvm) export RBJ=${RJH}jre/lib/i386 g++ appelMV.o -L $RBJ -ljava -lverify -L $RBJ/client -ljvm # exécution export LD_LIBRARY_PATH=${RJH}jre/lib/i386:${RJH}jre/lib/i386/client a.out */ #include <jni.h> #include <stdlib.h> #ifdef _WIN32 #define SEP_REPERTOIRE ';' // séparateur de répertoires #else /* UNIX */ #define SEP_REPERTOIRE ':' #endif #define REP_MES_CLASSES "." // répertoire des classes à charger int main(int nm, char * tm[]) { JNIEnv *penv; JavaVM *jvm; JDK1_1InitArgs vm_args; jint res; jclass i_cls; // identité de la classe jmethodID i_mth; // identité de la méthode jstring j_ch; // pour créer une chaîne 'java' jobjectArray j_to; // pour tableau d'objets 'java' char repertoires[2048]; // IMPORTANT: pour version 1.1 vm_args.version = 0x00010001; // pour version 1.2 vm_args.version = JNI_VERSION_1_2; JNI_GetDefaultJavaVMInitArgs(&vm_args); /* Pour charger les classes, ajouter REP_MES_CLASSES après CLASSPATH */ sprintf(repertoires, "%s%c%s", vm_args.classpath, SEP_REPERTOIRE, REP_MES_CLASSES); vm_args.classpath = repertoires; // Si options, pour la MV, récupérées de la ligne de commande // vm_args.options = options; // vm_args.nOptions = dwJLen; // vm_args.ignoreUnrecognized = TRUE; /* Créer la machine virtuelle Java */ // res = JNI_CreateJavaVM(&jvm,&penv,&vm_args); res = JNI_CreateJavaVM(&jvm,(void **)&penv,&vm_args); if (res < 0) { fprintf(stderr, "Mchine virtuelle non démarée !!!\n"); exit(1); } // Récupérer la classe i_cls = penv->FindClass("AppelMV"); if (i_cls == 0) { fprintf(stderr, "Classe AppelMV non trouvée\n"); exit(2); } // Récupérer la méthode 'static void main(String [])' i_mth = penv->GetStaticMethodID(i_cls, "main", "([Ljava/lang/String;)V"); if (i_mth == 0) { fprintf(stderr, "Méthode AppelMV.main non trouvée\n"); exit(3); } // Créer le paramètre de la méthode 'static void main(String [])' j_ch = penv->NewStringUTF(" depuis C++ !"); if (j_ch == 0) { fprintf(stderr, "Plus de mémoire !!!\n"); exit(4); } j_to = penv->NewObjectArray(1, penv->FindClass("java/lang/String"), j_ch); if (j_to == 0) { fprintf(stderr, "Plus de mémoire !!!\n"); exit(1); } // Enfin ... l'appel penv->CallStaticVoidMethod(i_cls, i_mth, j_to); jvm->DestroyJavaVM(); } /* Toutes les méthodes appartiennent à la classe JNIEnv Utiliser un champ d'un objet Le champ est défini, dans la classe, par son nom; la signature du champ permet des vérifications. Pour accéder à la valeur, il faut référencer l'objet concerné. identité du champ: jfieldID GetFieldID(jclass classe,char nom[],char signature[]) valeur du champ (récupération ou modification): j<BO> GetObjectField(jobject obj, jfieldID i_champ) void SetObjectField(jobject obj, jfieldID i_champ, jobject val) Utilisation d'une méthode Un méthode est définie par son nom et sa signature (dans le cas de surcharge la signature permet une identifdication unique). Une fois la méthode identifiée, dans la classe, on peut l'appeler. identité de la méthode: jmethodID GetStaticMethodID(jclass classe, const char nom[], const char signature[]) appels jobject Call<BOV>Method(jobject obj, jmethodID i_methode, ...) jobject Call<BOV>MethodV(jobject obj, jmethodID i_methodID, va_list args) jobject Call<BOV>MethodA(jobject obj, jmethodID i_methodID, jvalue targ[]) Utilisation d'une chaîne de caractères: jstring Deux codages en C: Unicode ou UTF-8; le type char se raproche de UTF-8; en C++ on aura deux longueurs (en tant qu'unicode' ou 'UTF-8') et deux tableaux accessibles( type jchar[] ou char[]). (cf http://www.uzine.net/article1785.html) création d'une chaîne java, à partir d'un tableau de caractères ! jstring NewString(const jchar *unicode, jsize longueur) ! jstring NewStringUTF(char tc[]) utilisation // Chaînes unicode jsize GetStringLength(jstring ch_java) const jchar* GetStringChars(jstring ch_java, jboolean *isCopy) // Chaînes utf-8 jsize GetStringUTFLength(jstring ch_java) { const char* GetStringUTFChars(jstring ch_java, jboolean *isCopy) gestion mémoire void ReleaseStringChars(jstring ch_java, const jchar *chars) void ReleaseStringUTFChars(jstring ch_java, const char tc[]) Utilisation d'un tableau: jarray Les tableaux envisagés ont des éléments d'un type primitif ou du type objet: jboolean, jbyte, jchar, jshort int jlong, jfloat, jdouble, jobjet Un tableau java est représenté par "un descripteur" contenant entre autre sa taille et l'adresse de la zone mémoire où sont placés les éléments. Ne pas confondre les types des descripteurs: jbooleanArray,Array jbyteArray ... jobjetArray et les types des tableaux d'éléments en C++, qui sont respectivement jboolean *, jbyte * ... jobjet * L'utilisation des tableaux d'objets est plus limitée que pour les types primitifs; on commence par exposer les méthodes sur ces types. création d'un tableau au sens java: jintArray NewIntArray(jsize taille) idem pour jboolean, jbyte ... jdouble accès à la taille, à l'adresse, à un sous-tableau jsize GetArrayLength(jarray array) jint * GetIntArrayElements(jintArray t_java, jboolean *isCopy) void GetIntArrayRegion(jintArray t_java, jsize debut, jsize longueur, jint sousTableau[]) void SetIntArrayRegion(jintArray t_java, jsize debut, jsize longueur, jint sousTableau[]) { idem pour jboolean, jbyte ... jdouble gestion mémoire java et C++ et recopie void ReleaseIntArrayElements(jintArray t_j, jint t[], jint mode) idem pour jboolean, jbyte ... jdouble cas des tableaux d'objets jobjectArray NewObjectArray(jsize taille) jobject GetObjectArrayElement(jobjectArray t_j, jsize index) void SetObjectArrayElement(jobjectArray t_j, jsize index, jobject val) */
Dans les commandes ci-dessous on dénomme Truc.java un source
java définissant une classe, truc.h les déclarations associées
en langage C++ et truc.C le fichier contenant le langage C++
codant les méthodes appelées depuis 'java'.
On a considéré que la bibliothèque partagée, fonctionnant avec
la machine virtuelle java, était dans le sous-répertoire
lib du répertoire courant.
L'utilisation, en langage C, d'une information codée dans la machine virtuelle Java est plus ou moins directe suivant sa complexité. Les types et les fonctions en C de l'interface JNI, qui permettent cette utilisation, sont rappelées ci-après.
Le cas simple correspond à l'un des 8 types basiques du langage Java: boolean,
byte, char, short, int, long, float et double. le type int est pris
comme exemple, mais la transposition aux autres types est évidente.
Les cas suivants correspondent aux tableaux d'éléments de type
basique et aux chaînes de la classe String de Java, manipulés
par l'intermédiaire de descripteurs.
Enfin le cas d'utilisation des champs et méthodes d'une classe Java
quelconque, et le cas des tableaux d'objets sont les plus complexes.
Voici un résumé des mots à utiliser avec JNI, suivant l'information à traiter.
Les tables suivantes, extraites du site Sun, fournissent les mots définis par la bibliothèque JNI
The following two tables summarize how Java types map to native types and the rules for encoding Java types in native signatures.
Primitive Types and Native Equivalents
Java Type Native Type Size in bits boolean
jboolean
8, unsigned byte
jbyte
8 char
jchar
16, unsigned short
jshort
16 int
jint
32 long
jlong
64 float
jfloat
32 double
jdouble
64 void
void
n/a
Signature | Java Programming Language Type |
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L fully-qualified-class ; | fully-qualified-class |
[ type | type[] |
( arg-types ) ret-type | method type |
GetStringChars
GetArrayLength
GetObjectClass
GetFieldID
ExceptionClear
NewGlobalRef
MonitorEnter