Java Language
Interface native Java
Recherche…
Paramètres
Paramètre | Détails |
---|---|
JNIEnv | Pointeur vers l'environnement JNI |
jobject | L'objet qui a appelé la méthode native non static |
jclass | La classe qui a invoqué la méthode native static |
Remarques
La configuration de JNI nécessite à la fois un compilateur Java et un compilateur natif. Selon l'IDE et le système d'exploitation, une configuration est requise. Un guide pour Eclipse peut être trouvé ici . Un tutoriel complet peut être trouvé ici .
Voici les étapes à suivre pour configurer le lien Java-C ++ sur Windows:
- Compilez les fichiers source Java (
.java
) dans les classes (.class
) en utilisantjavac
. - Créez des fichiers d'en-tête (
.h
) à partir des classes Java contenantnative
méthodesnative
utilisantjavah
. Ces fichiers "instruisent" le code natif dont il est responsable de l'implémentation. - Incluez les fichiers d'en-tête (
#include
) dans les fichiers source C ++ (.cpp
) implémentant les méthodesnative
. - Compilez les fichiers source C ++ et créez une bibliothèque (
.dll
). Cette bibliothèque contient l'implémentation du code natif. - Spécifiez le chemin de la bibliothèque (
-Djava.library.path
) et chargez-le dans le fichier source Java (System.loadLibrary(...)
).
Les rappels (appel des méthodes Java à partir du code natif) nécessitent de spécifier un descripteur de méthode. Si le descripteur est incorrect, une erreur d'exécution se produit. Pour cette raison, il est utile de faire les descripteurs pour nous, cela peut être fait avec javap -s
.
Appel des méthodes C ++ à partir de Java
Les méthodes statiques et membres de Java peuvent être marquées comme natives pour indiquer que leur implémentation se trouve dans un fichier de bibliothèque partagé. Lors de l'exécution d'une méthode native, la JVM recherche une fonction correspondante dans les bibliothèques chargées (voir Chargement de bibliothèques natives ) en utilisant un simple schéma de gestion des noms, effectue la conversion des arguments et la pile.
Code Java
/*** com/example/jni/JNIJava.java **/
package com.example.jni;
public class JNIJava {
static {
System.loadLibrary("libJNI_CPP");
}
// Obviously, native methods may not have a body defined in Java
public native void printString(String name);
public static native double average(int[] nums);
public static void main(final String[] args) {
JNIJava jniJava = new JNIJava();
jniJava.printString("Invoked C++ 'printString' from Java");
double d = average(new int[]{1, 2, 3, 4, 7});
System.out.println("Got result from C++ 'average': " + d);
}
}
Code C ++
Les fichiers d'en-tête contenant des déclarations de fonctions natives doivent être générés à l'aide de l'outil javah
sur les classes cibles. Exécuter la commande suivante dans le répertoire de construction:
javah -o com_example_jni_JNIJava.hpp com.example.jni.JNIJava
... produit le fichier d'en-tête suivant ( commentaires supprimés pour des raisons de concision ):
// com_example_jni_JNIJava.hpp
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // The JNI API declarations
#ifndef _Included_com_example_jni_JNIJava
#define _Included_com_example_jni_JNIJava
#ifdef __cplusplus
extern "C" { // This is absolutely required if using a C++ compiler
#endif
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString
(JNIEnv *, jobject, jstring);
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average
(JNIEnv *, jclass, jintArray);
#ifdef __cplusplus
}
#endif
#endif
Voici un exemple d'implémentation:
// com_example_jni_JNIJava.cpp
#include <iostream>
#include "com_example_jni_JNIJava.hpp"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString(JNIEnv *env, jobject jthis, jstring string) {
const char *stringInC = env->GetStringUTFChars(string, NULL);
if (NULL == stringInC)
return;
cout << stringInC << endl;
env->ReleaseStringUTFChars(string, stringInC);
}
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average(JNIEnv *env, jclass jthis, jintArray intArray) {
jint *intArrayInC = env->GetIntArrayElements(intArray, NULL);
if (NULL == intArrayInC)
return -1;
jsize length = env->GetArrayLength(intArray);
int sum = 0;
for (int i = 0; i < length; i++) {
sum += intArrayInC[i];
}
env->ReleaseIntArrayElements(intArray, intArrayInC, 0);
return (double) sum / length;
}
Sortie
L'exécution de la classe d'exemple ci-dessus génère la sortie suivante:
C ++ appelé "printString" depuis Java
Vous avez obtenu le résultat de la moyenne C ++: 3.4
Appeler les méthodes Java à partir de C ++ (rappel)
L'appel d'une méthode Java à partir de code natif est un processus en deux étapes:
- obtenir un pointeur de méthode avec la fonction JNI
GetMethodID
, en utilisant le nom de la méthode et le descripteur; - appelez l'une des fonctions de la
Call*Method
répertoriées ici .
Code Java
/*** com.example.jni.JNIJavaCallback.java ***/
package com.example.jni;
public class JNIJavaCallback {
static {
System.loadLibrary("libJNI_CPP");
}
public static void main(String[] args) {
new JNIJavaCallback().callback();
}
public native void callback();
public static void printNum(int i) {
System.out.println("Got int from C++: " + i);
}
public void printFloat(float i) {
System.out.println("Got float from C++: " + i);
}
}
Code C ++
// com_example_jni_JNICppCallback.cpp
#include <iostream>
#include "com_example_jni_JNIJavaCallback.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJavaCallback_callback(JNIEnv *env, jobject jthis) {
jclass thisClass = env->GetObjectClass(jthis);
jmethodID printFloat = env->GetMethodID(thisClass, "printFloat", "(F)V");
if (NULL == printFloat)
return;
env->CallVoidMethod(jthis, printFloat, 5.221);
jmethodID staticPrintInt = env->GetStaticMethodID(thisClass, "printNum", "(I)V");
if (NULL == staticPrintInt)
return;
env->CallVoidMethod(jthis, staticPrintInt, 17);
}
Sortie
Got float de C ++: 5.221
Got int de C ++: 17
Obtenir le descripteur
Les descripteurs (ou signatures de type internes ) sont obtenus à l'aide du programme javap sur le fichier .class
compilé. Voici la sortie de javap -p -s com.example.jni.JNIJavaCallback
:
Compiled from "JNIJavaCallback.java"
public class com.example.jni.JNIJavaCallback {
static {};
descriptor: ()V
public com.example.jni.JNIJavaCallback();
descriptor: ()V
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
public native void callback();
descriptor: ()V
public static void printNum(int);
descriptor: (I)V // <---- Needed
public void printFloat(float);
descriptor: (F)V // <---- Needed
}
Chargement de bibliothèques natives
L'idiome commun pour charger des fichiers de bibliothèque partagée en Java est le suivant:
public class ClassWithNativeMethods {
static {
System.loadLibrary("Example");
}
public native void someNativeMethod(String arg);
...
Les appels à System.loadLibrary
sont presque toujours statiques pour se produire lors du chargement de la classe, garantissant qu'aucune méthode native ne peut s'exécuter avant le chargement de la bibliothèque partagée. Cependant, ce qui suit est possible:
public class ClassWithNativeMethods {
// Call this before using any native method
public static void prepareNativeMethods() {
System.loadLibrary("Example");
}
...
Cela permet de différer le chargement de la bibliothèque partagée jusqu'à ce que cela soit nécessaire, mais nécessite un soin particulier pour éviter java.lang.UnsatisfiedLinkError
s.
Recherche de fichier cible
Les fichiers de bibliothèque partagés sont recherchés dans les chemins définis par la propriété système java.library.path
, qui peuvent être -Djava.library.path=
l'aide de l'argument -Djava.library.path=
JVM au moment de l'exécution:
java -Djava.library.path=path/to/lib/:path/to/other/lib MainClassWithNativeMethods
Attention aux séparateurs de chemin système: par exemple, Windows utilise ;
au lieu de :
Notez que System.loadLibrary
résout les noms de fichiers de bibliothèque en fonction de la plate-forme: l'extrait de code ci-dessus attend un fichier nommé libExample.so
sous Linux et Example.dll
sous Windows.
Une alternative à System.loadLibrary
est System.load(String)
, qui prend le chemin d'accès complet à un fichier de bibliothèque partagée, contournant la recherche java.library.path
:
public class ClassWithNativeMethods {
static {
System.load("/path/to/lib/libExample.so");
}
...