2010-10-13 21 views
54

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á/liberar un puntero al mismo tipo de estructura para que sea un poco más fácil de tratar mi envoltorio Sorprendentemente, la documentación de JNI dice muy poco sobre cómo lidiar con las estructuras de C.¿Cómo pasar estructuras C de ida y vuelta al código de Java en JNI?

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 de envoltura 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, la 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 presionar 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.

+0

¿Algo más específico sobre el error? Cualquier mensaje de error? –

+0

No, solo bloquea el JRE. Esa es una trampa sobre tratar con JNI ... –

+1

La característica CheckJNI se agregó para encontrar errores de código comunes antes de que se conviertan en Heisenbugs. Está habilitado por defecto en el emulador. Consulte los jni-tips y los documentos embedded-vm-control en http://android.git.kernel.org/?p=platform/dalvik.git;a=tree;f=docs;h=b0d3023109f548626cce1a9a586c95e82fb012ac;hb=HEAD para información sobre cómo habilitarlo en un dispositivo. – fadden

Respuesta

35

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, mucha mano de obra, con suerte ya existen programas que lo hacen 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.

+11

¿Podría pegar algunos ejemplos de código simple de dicha integración? – Centurion

+4

@Centurion Sé que las URL generalmente son mal vistas, pero este es un comentario y encontré esta página web. Muy útil para descifrar esto: http://www3.ntu.edu.sg/home/ehchua/programming/java/ JavaNativeInterface.html # zz-5.1 – James

8

Se bloquea porque Java_com_myorg_MyJavaClass_createNewMyStruct se declara que devuelve "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 jobject con funciones JNI. Asigne los objetos desde Java o con NewObject, obtenga/establezca los campos del objeto con las llamadas apropiadas.

Aquí hay varias formas de "hacer trampa". 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.

6

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 JNI, para mantener el puntero/estructura en nativo y se recomienda la manipulación de exportación a Java. Puedes leer algunos artículos útiles. The JavaTM Native Interface Programmer's Guide and Specification, He leído. 9.5 Peer Classes tienen una solución para manejarlo.

+2

El enlace está muerto: parece que Oracle tomó los contenidos. Hay algunos sitios con mirrors, o puede usar archive.org: http://web.archive.org/web/20070101174413/http://java.sun.com/docs/books/jni/ – fadden

-1
  1. Realice la clase tanto en el lado de Java como en el de C++, solo agregue las variables de 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.
  2. Usa tu (s) IDE (s) para hacer setters y getters para las variables miembro automáticamente.
  3. Use javah para generar el archivo de encabezado C de la clase Java.
  4. Realice una cierta edición en el lado de C++ para hacer que los ajustadores y captadores coincidan con el archivo de encabezado generado.
  5. 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.

Cuestiones relacionadas