nativos - ¿Cómo pasar estructuras C de ida y vuelta al código Java en JNI?
metodos nativos en java (4)
Tengo algunas funciones C a las que llamo a través de JNI que toman un puntero a una estructura, y algunas otras funciones que asignan / liberan un puntero al mismo tipo de estructura para que sea un poco más fácil tratar con mi envoltorio . Sorprendentemente, la documentación de JNI dice muy poco sobre cómo lidiar con las estructuras de C.
Mi archivo de encabezado C se ve así:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
El archivo JNI C wrapper 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, la clase correspondiente de 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 createNewMyStruct()
. Soy un poco nuevo para 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 en funcionamiento. Además, este proyecto utiliza el marco Android NDK, 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 ... parece un error de configuración / inicialización de JNI por mi parte.
- Realice la clase tanto en Java como en C ++, solo agregue las variables miembro. Las estructuras de C ++ son realmente solo clases con miembros de datos públicos. Si realmente estás en C pura, deja de leer ahora.
- Use su (s) IDE (s) para hacer setters y getters para las variables miembro automáticamente.
- Use javah para generar el archivo de encabezado C de la clase Java.
- Haga algo de edición en el lado de C ++ para hacer que los setters y getters coincidan con el archivo de encabezado generado.
- Ponga 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 suceda. La mayoría de los IDE ni siquiera admiten proyectos de idiomas mixtos, y mucho menos hacerlos hablar entre ellos.
La estructura C es la colección de variables (algunas son puntero a la función). Pasar a Java no es una buena idea. En general, es el problema de cómo pasar el tipo más complejo a Java, como el puntero.
En el libro de JNI, se recomienda mantener el puntero / estructura en nativo y exportar la manipulación a java. Puedes leer algunos artículos útiles. La Guía y la Especificación del Programador de Interfaz Nativa JavaTM, he leído. 9.5 Las clases pares tienen una solución para manejarlo.
Necesita crear una clase Java con los mismos miembros que C struct, y ''mapearlos'' en el código C a través de los métodos env-> GetIntField, env-> SetIntField, env-> GetFloatField, env-> SetFloatField, y así sucesivamente - en resumen, mucho trabajo manual, ojalá 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 usted.
Se está bloqueando porque Java_com_myorg_MyJavaClass_createNewMyStruct se declara para devolver "jobject", pero en realidad está devolviendo struct MyStruct. Si ejecutó esto con CheckJNI habilitado, la máquina virtual se quejaría en voz alta y abortaría. Su función processData () también va a estar bastante molesta por lo que se entrega en "argumentos".
Un jobject es un objeto en el montón administrado. Puede tener elementos adicionales antes o después de los campos declarados, y los campos no tienen que estar dispuestos en la memoria en ningún orden en particular. Entonces no puedes mapear una estructura C encima de una clase Java.
La forma más directa de tratar con esto se identificó en una respuesta anterior: manipular el proyecto con funciones JNI. Asigne los objetos desde Java o con NewObject, obtenga / establezca los campos del objeto con las llamadas apropiadas.
Hay varias formas de "hacer trampa" aquí. Por ejemplo, podría incluir un byte [] en su objeto Java que contenga sizeof (struct MyStruct) bytes y luego use GetByteArrayElements para obtener un puntero al mismo. Un poco feo, especialmente si también quieres acceder a los campos desde Java.