¿Cómo pasar estructuras C de ida y vuelta al código Java en JNI?


Tengo algunas funciones C que estoy llamando a través de JNI que toman un puntero a una estructura, y algunas otras funciones que asignarán/liberarán un puntero al mismo tipo de estructura para que sea un poco más fácil tratar con mi envoltura. Sorprendentemente, la documentación del JNI dice muy poco sobre cómo tratar las estructuras C.

Mi archivo de cabecera C se ve así:

typedef struct _MyStruct {
  float member;
} MyStruct;

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

El archivo contenedor JNI C correspondiente contiene:

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);
}

...y finalmente, el correspondiente Clase Java:

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);
  }
}

Desafortunadamente, este código bloquea la JVM justo después de golpear createNewMyStruct(). Soy un poco nuevo en el JNI y no tengo idea de cuál podría ser el problema.

Editar: Debo señalar que el código C es muy vainilla C, está bien probado y fue portado desde un proyecto de iPhone que funciona. Además, este proyecto utiliza el marco NDK de Android, que le permite ejecutar código C nativo desde un proyecto de Android desde JNI. Sin embargo, no creo que esto sea estrictamente un problema de NDK... se parece un error de configuración/inicialización de JNI de mi parte.

Author: Nik Reiman, 2010-10-13

4 answers

Necesita crear una clase Java con los mismos miembros que C struct, y' mapearlos ' en el código C a través de métodos env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, y así sucesivamente - en resumen, mucho trabajo manual, esperemos que ya existan programas que lo hagan automáticamente: JNAerator ( http://code.google.com/p/jnaerator ) y SWIG ( http://www.swig.org / ). Ambos tienen sus pros y sus contras, la elección depende de ti.

 38
Author: iirekm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-13 14:29:53

Se bloquea porque Java_com_myorg_MyJavaClass_createNewMyStruct se declara que devuelve jobject, pero en realidad devuelve struct MyStruct. Si ejecuta esto con CheckJNI habilitado, la máquina virtual se quejará en voz alta y abortará. Su processData() función también va a ser bastante molesto acerca de lo que se entrega en arguments.

Un jobject es un objeto en el montón administrado. Puede tener cosas adicionales antes o después de los campos declarados, y los campos no tienen que estar dispuestos en memoria en ningún orden en particular. Así que no puedes mapear una estructura C encima de una clase Java.

La forma más directa de lidiar con esto se identificó en una respuesta anterior: manipular el jobject con funciones JNI. Asignar los objetos desde Java o con NewObject, Get/Set los campos objeto con las llamadas apropiadas.

Hay varias maneras de "engañar" aquí. Por ejemplo, podría incluir un byte[] en su objeto Java que contenga sizeof(struct MyStruct) bytes y luego usar GetByteArrayElements para obtener un puntero a él. Un poco feo, especialmente si quieres acceder a la campos del lado de Java también.

 9
Author: fadden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-24 15:05:16

La estructura C es la colección de variables (algunas son puntero de función). Pasar a Java no es una buena idea. En general, es el problema cómo pasar tipos más complejos a java, como pointer.

En JNI book, se recomienda mantener el puntero/estructura en la manipulación nativa y exportar a java. Puedes leer algunos artículos útiles. La Guía y Especificación del Programador de Interfaz Nativa JavaTM, he leído. 9.5 Las clases de pares tienen una solución para lidiar con ello.

 6
Author: qrtt1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-23 02:30:50
  1. Haga la clase en ambos lados Java y C++, simplemente poniendo las variables miembro. Las estructuras de C++ son realmente solo clases con miembros de datos públicos. Si realmente estás en C puro, deja de leer ahora.
  2. Use su IDE(s) para hacer setters y getters para las variables miembro automáticamente.
  3. Utilice javah para generar el archivo de cabecera C a partir de la clase Java.
  4. Haga algunas ediciones en el lado de C++ para hacer que los setters y getters coincidan con el archivo de encabezado generado.
  5. Poner en el código JNI.

Esta no es una solución ideal, pero puede ahorrarle un poco de tiempo, y al menos le dará un esqueleto que puede editar. Esta funcionalidad se podría agregar a un IDE, pero sin una gran demanda, probablemente no sucederá. La mayoría de los IDE ni siquiera admiten proyectos de lenguaje mixto, y mucho menos hacer que hablen entre ellos.

 -1
Author: Bill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 12:27:50