JNA parece bastante más fácil de usar para llamar a código nativo en comparación con JNI. ¿En qué casos usaría JNI sobre JNA?
JNA parece bastante más fácil de usar para llamar a código nativo en comparación con JNI. ¿En qué casos usaría JNI sobre JNA?
Respuestas:
Estos son los problemas que he encontrado. Quizás haya más. Pero, en general, el rendimiento no es tan diferente entre jna y jni, por lo que siempre que pueda usar JNA, utilícelo.
EDITAR
Esta respuesta parece ser bastante popular. Así que aquí hay algunas adiciones:
Entonces, sigo creyendo que siempre que sea posible, es mejor usar JNA o BridJ, y volver a jni si el rendimiento es crítico, porque si necesita llamar a funciones nativas con frecuencia, el impacto en el rendimiento es notable.
JNIEXPORT
funciones globales . Actualmente estoy explorando JavaCpp como una opción, que usa JNI, pero no creo que Vanilla JNI admita esto. ¿Hay alguna forma de llamar a las funciones miembro de C ++ usando Vanilla JNI que estoy pasando por alto?
Es difícil responder a una pregunta tan genérica. Supongo que la diferencia más obvia es que con JNI, la conversión de tipos se implementa en el lado nativo del borde Java / nativo, mientras que con JNA, la conversión de tipos se implementa en Java. Si ya se siente bastante cómodo con la programación en C y tiene que implementar algún código nativo usted mismo, supongo que JNI no parecerá demasiado complejo. Si usted es un programador de Java y solo necesita invocar una biblioteca nativa de terceros, usar JNA es probablemente la ruta más fácil para evitar los problemas quizás no tan obvios con JNI.
Aunque nunca he comparado ninguna diferencia, lo haría debido al diseño, al menos supongo que la conversión de tipos con JNA en algunas situaciones funcionará peor que con JNI. Por ejemplo, al pasar matrices, JNA las convertirá de Java a nativas al comienzo de cada llamada a la función y de regreso al final de la llamada a la función. Con JNI, puede controlarse a sí mismo cuando se genera una "vista" nativa de la matriz, potencialmente creando solo una vista de una parte de la matriz, mantener la vista en varias llamadas de función y al final liberar la vista y decidir si desea para mantener los cambios (posiblemente requiriendo volver a copiar los datos) o descartar los cambios (no se requiere copia). Sé que puede usar una matriz nativa en las llamadas a funciones con JNA usando la clase Memory, pero esto también requerirá copia de memoria, que puede ser innecesario con JNI. Es posible que la diferencia no sea relevante, pero si su objetivo original es aumentar el rendimiento de la aplicación implementando partes de ella en código nativo, el uso de una tecnología de puente de peor rendimiento parece no ser la opción más obvia.
Eso es solo lo que se me ocurre, aunque no soy un gran usuario de ninguno de los dos. También parece que podría evitar JNA si quisiera una interfaz mejor que la que proporcionan, pero podría codificar eso en Java.
Por cierto, en uno de nuestros proyectos, mantuvimos una huella JNI muy pequeña. Usamos búferes de protocolo para representar nuestros objetos de dominio y, por lo tanto, solo teníamos una función nativa para unir Java y C (luego, por supuesto, esa función C llamaría a un montón de otras funciones).
No es una respuesta directa y no tengo experiencia con JNA pero, cuando miro los Proyectos que usan JNA y veo nombres como SVNKit, IntelliJ IDEA, NetBeans IDE, etc., tiendo a creer que es una biblioteca bastante decente.
En realidad, definitivamente creo que habría usado JNA en lugar de JNI cuando tuve que hacerlo, ya que de hecho parece más simple que JNI (que tiene un proceso de desarrollo aburrido). Lástima, JNA no fue lanzado en este momento.
Si desea el rendimiento de JNI pero su complejidad lo intimida, puede considerar el uso de herramientas que generen enlaces JNI automáticamente. Por ejemplo, JANET (descargo de responsabilidad: lo escribí) le permite mezclar código Java y C ++ en un solo archivo fuente y, por ejemplo, realizar llamadas de C ++ a Java utilizando la sintaxis estándar de Java. Por ejemplo, así es como imprimiría una cadena C en la salida estándar de Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
JANET luego traduce el Java incrustado con comillas invertidas en las llamadas JNI apropiadas.
De hecho, hice algunos puntos de referencia simples con JNI y JNA.
Como ya han señalado otros, JNA es por conveniencia. No es necesario compilar o escribir código nativo cuando se usa JNA. El cargador de biblioteca nativo de JNA también es uno de los mejores y más fáciles de usar que he visto. Lamentablemente, parece que no puedes usarlo para JNI. (Es por eso que escribí una alternativa para System.loadLibrary () que usa la convención de ruta de JNA y admite la carga sin problemas desde la ruta de clase (es decir, jars)).
Sin embargo, el rendimiento de JNA puede ser mucho peor que el de JNI. Hice una prueba muy simple que llamó a una función de incremento de enteros nativa simple "return arg + 1;". Los puntos de referencia realizados con jmh mostraron que las llamadas JNI a esa función son 15 veces más rápidas que JNA.
Un ejemplo más "complejo" en el que la función nativa suma una matriz entera de 4 valores aún mostró que el rendimiento de JNI es 3 veces más rápido que JNA. La ventaja reducida probablemente se debió a la forma en que accede a las matrices en JNI: mi ejemplo creó algunas cosas y las lanzó nuevamente durante cada operación de suma.
El código y los resultados de las pruebas se pueden encontrar en github .
Investigué JNI y JNA para comparar el rendimiento porque necesitábamos decidir que uno de ellos llamara a un dll en el proyecto y teníamos una restricción de tiempo real. Los resultados han demostrado que JNI tiene un rendimiento mayor que JNA (aproximadamente 40 veces). Tal vez haya un truco para un mejor rendimiento en JNA, pero es muy lento para un ejemplo simple.
A menos que me falte algo, ¿no es la principal diferencia entre JNA vs JNI que con JNA no se puede llamar al código Java desde el código nativo (C)?
En mi aplicación específica, JNI resultó mucho más fácil de usar. Necesitaba leer y escribir transmisiones continuas desde y hacia un puerto serie, y nada más. En lugar de intentar aprender la infraestructura muy involucrada en JNA, me resultó mucho más fácil crear un prototipo de la interfaz nativa en Windows con una DLL de propósito especial que exportaba solo seis funciones: