Java:JNI

De WIKI.minetti.org
Révision de 7 avril 2016 à 17:57 par Jp (discussion | contributions) (Page créée avec « == But == Java Native Interface (JNI) permet à une application Java d'exécuter du code natif placé dans des bibliothèques chargées dynamiquement (DLL sous Windows et... »)

(diff) ← Version précédente | Voir la version courante (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

But

Java Native Interface (JNI) permet à une application Java d'exécuter du code natif placé dans des bibliothèques chargées dynamiquement (DLL sous Windows et SO sous Linux).

JNI est bien adapté pour accéder à ses propres bibliothèques. Par contre si l'on doit accéder à des bibliothèques tiers, JNA est mieux adapté.

Pré-requis

Pas de pré-requis si ce n'est qu'une plateforme de développement C++ opérationnelle.

Mode opératoire

  • Déclarer les méthodes natives dans le code Java:
public native void myMethodA();
  • Implémenter le chargement de la bibliothèque dans le code d'initialisation statique de la classe (celle qui contient les déclarations des méthodes natives) - sous MS Windows le fichier se nommera myLibrary.dll et myLibrary.so sous Linux:
static {
  System.loadLibrary("myLibrary");
}
  • Compiler la classe Java:
javac MyClass.java
  • Générer les fichier d'en-tête C (extension .h) en exécutant la commande:
javah -jni MyClass
  • Implémenter le code natif en C.
  • Compiler le code natif en C:
gcc -c -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -o MyClass.o MyClass.c
  • Créer le fichier .def correspondant à la bibliothèque:
EXPORTS
Java_MyClass_myMethodA
  • Créer la bibliothèque:
gcc -shared -o myLibrary.dll MyClass.o MyClass.def

Appel d'objet Java passé en paramètre

Il est possible d'appeler des méthodes d'un objet Java passé en paramètre. Par exemple, si nous avons la fonction native suivante:

public native void loadDeviceTree (final UsbDeviceTreeBuilder builder);

La fonction C aura la signature suivante:

JNIEXPORT void JNICALL Java_org_minetti_test_JniLibrary_loadDeviceTree(JNIEnv *env, jobject obj, jobject builderObject)

Dans le source C de la bibliothèque, notre objet sera du type jobject. Pour appeler une méthode quelconque de cette objet, il faudra d'abord déterminer sa signature en tapant la commande suivante à partir du répertoire où sont compilés les classes Java (ici, le répertoire Maven):

cd target\classes
javap -s -p org.minetti.test.UsbDeviceTreeBuilder

Par exemple, ça pourrait donner ceci:

Compiled from "UsbDeviceTreeBuilder.java"
public final class org.minetti.test.UsbDeviceTreeBuilder extends java.lang.Object{
private int level;
  Signature: I
public org.minetti.test.UsbDeviceTreeBuilder();
  Signature: ()V
public void down();
  Signature: ()V
public void up();
  Signature: ()V
public void addHostController(java.lang.String);
  Signature: (Ljava/lang/String;)V
public void addRootHub();
  Signature: ()V
public void addHub(int, int, java.lang.String, java.lang.String, int, java.lang.String, short, short, short);
  Signature: (IILjava/lang/String;Ljava/lang/String;ILjava/lang/String;SSS)V
public void add(int, int, java.lang.String, java.lang.String, int, java.lang.String, short, short, short);
  Signature: (IILjava/lang/String;Ljava/lang/String;ILjava/lang/String;SSS)V
}

Ensuite, nous revenons à notre source C afin d'implémenter, par exemple, l'appel à la méthode addHostController de notre objet UsbDeviceTreeBuilder (signature: (Ljava/lang/String;)V):

jclass builderClass = env->GetObjectClass(builderObject);
jmethodID addHostControllerMethod = env->GetMethodID(builderClass, "addHostController", "(Ljava/lang/String;)V");
jstring name = env->NewStringUTF("MyName");
env->CallVoidMethod(builderObject, addHostControllerMethod, name);

Conversions types C en Java

  • Pour convertir une chaîne de caractère codé en UTF-8:
char *cStr = "Exemple de chaîne de caractères";
jstring javaStr = env->NewStringUTF(cStr);
  • Pour convertir une chaîne de caractère Unicode:
WCHAR *cStr = L"Exemple de chaîne de caractères";
jstring javaStr = env->NewString((const jchar*)buf, wcslen(buf));

Gestion des exceptions

Le code C++ suivant permet d'intercepter les exceptions C pour les renvoyer dans le code Java:

try {
  ...
}
catch (const char *message) {
  env->ThrowNew(env->FindClass("java/lang/Exception"), message);
}

Ici, l'exception renvoyée sera de type java.lang.Exception. Ce code doit être obligatoirement placé dans la fonction appelé par le code Java (c'est à dire la fonction native).