64 votes

Comment passer des structures C en arrière au code Java dans JNI?

J'ai quelques fonctions C qui je suis en appel par JNI qui prennent un pointeur sur une structure, et quelques autres fonctions qui allouera/libérer un pointeur vers le même type de structure, de sorte qu'il est un peu plus facile à traiter avec mon wrapper. Étonnamment, la JNI documentation dit très peu de choses sur la façon de traiter avec les structures C.

Mon C fichier d'en-tête ressemble à ceci:

typedef struct _MyStruct {
  float member;
} MyStruct;

MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);

Le correspondant de la JNI C wrapper fichier contient:

JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
  return createNewMyStruct();
}

JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
                                       jint numObjects, jobject arguments) {
  int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
  processData(actualData, numObjects, arguments);
  (*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}

...et enfin, la classe Java correspondante:

public class MyJavaClass {
  static { System.loadLibrary("MyJniLibrary"); }

  private native MyStruct createNewMyStruct();
  private native void processData(int[] data, int numObjects, MyStruct arguments);

  private class MyStruct {
    float member;
  }

  public void test() {
    MyStruct foo = createNewMyStruct();
    foo.member = 3.14159f;
    int[] testData = new int[10];
    processData(testData, 10, foo);
  }
}

Malheureusement, ce code d'accidents de la JVM juste après frapper createNewMyStruct(). Je suis un peu nouveau pour JNI et n'ont aucune idée de ce que le problème pourrait être.

Edit: je tiens à noter que le code C est très vanille C, est bien testé et a été porté à partir d'un iPhone de projet. Aussi, ce projet est à l'aide de l'Android NDK cadre, ce qui vous permet d'exécuter le code C code à partir d'un projet Android à partir de l'intérieur de JNI. Cependant, je ne pense pas que c'est strictement un NDK question... il semble qu'un JNI programme d'installation/de l'initialisation erreur de ma part.

41voto

iirekm Points 2338

Vous devez créer une classe Java avec les mêmes membres que C struct, et "carte" dans le code C par l'intermédiaire des méthodes env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, et ainsi de suite, bref, beaucoup de travail manuel, je l'espère, il existe déjà des programmes qui le font automatiquement: JNAerator (http://code.google.com/p/jnaerator) et SWIG (http://www.swig.org/). Les deux ont leurs avantages et inconvénients, le choix est à vous.

10voto

fadden Points 17450

Il plante parce que Java_com_myorg_MyJavaClass_createnewmystruct est déclaré "retour aux jobject", mais est en fait le retour struct MyStruct. Si vous avez exécuté cette avec CheckJNI activé, la machine virtuelle puisse se plaindre bruyamment et d'abandon. Votre processData() la fonction est également va être assez contrarié à propos de ce qu'il obtient remis "arguments".

Un jobject est un objet sur le tas managé. Il peut avoir des trucs supplémentaires avant ou après la déclaration de champs, et les champs n'ont pas à être mises en mémoire dans un ordre particulier. Si vous ne pouvez pas mapper un struct C sur le haut d'une classe Java.

La façon la plus simple de traiter avec cela a été indiqué dans une précédente réponse: manipuler les jobject avec JNI fonctions. Allouer les objets de Java ou avec NewObject, Obtenir/Définir les champs d'objet avec des appels appropriés.

Il y a différentes manières de "tricher" ici. Par exemple, vous pouvez inclure un byte[] dans votre objet Java qui détient sizeof(struct MyStruct) octets et ensuite utiliser GetByteArrayElements pour obtenir un pointeur vers elle. Un peu moche, surtout si vous souhaitez accéder aux champs de la Java côté.

7voto

qrtt1 Points 2700

Structure C est la collection de variables (certains sont de pointeur de fonction). Passer à java n'est pas une bonne idée. En général, c'est le problème de la manière de passer les plus complexes de type de java, comme pointeur.

Dans JNI livre, pour garder le pointeur/structure native et l'exportation de manipulation de java est recommandé. Vous pouvez lire quelques articles utiles. La Java Native Interface Guide du Programmeur et le cahier des charges que j'ai lu. 9.5 Classes de Pairs ont une solution pour traiter avec elle.

0voto

Bill Points 358
  1. Faire la classe à la fois le Java et le C++ côtés, il suffit de le mettre dans les variables de membre. C++ structures sont vraiment juste des classes avec les données publiques des membres. Si vous êtes vraiment dans la pure C, arrêtez de lire maintenant.
  2. Utiliser un environnement de développement intégré(s) à faire des setters et getters pour les variables de membre automatiquement.
  3. Utilisation javah pour générer le fichier en-tête C de la classe Java.
  4. Faire quelques modification sur le C++ côté pour faire les setters et getters correspondance générée fichier d'en-tête.
  5. Mettre dans la JNI code.

Ce n'est pas une solution idéale, mais il peut vous faire économiser un peu de temps, et il va au moins vous donner un squelette que vous pouvez modifier. Cette fonctionnalité peut être ajoutée à un IDE, mais sans une forte demande, il ne sera probablement pas se produire. La plupart des IDEs ne supportent même mélangés les projets de langue, et encore moins d'avoir parler les uns aux autres.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X