2009-10-12 774 views
106

JNA parece un poco más fácil de usar para llamar al código nativo en comparación con JNI. ¿En qué casos usarías JNI sobre JNA?¿Utiliza JNI en lugar de JNA para llamar al código nativo?

+3

También pruebe BridJ. –

+0

Un factor importante que elegimos JNA sobre JNI, es que JNA no requirió ninguna modificación a nuestras bibliotecas nativas. Tener que personalizar las bibliotecas nativas solo para poder trabajar con Java fue un gran NO para nosotros. – kvr

Respuesta

119
  1. JNA no admite la asignación de clases de C++, por lo que si usted está utilizando biblioteca de C++ se necesita un envoltorio JNI
  2. Si necesita una gran cantidad de copias de la memoria. Por ejemplo, llama a un método que le devuelve un búfer de bytes grandes, cambia algo en él, luego necesita llamar a otro método que utiliza este búfer de bytes. Esto requeriría copiar este búfer de c a java, luego copiarlo de java a c. En este caso, jni ganará en rendimiento porque puede mantener y modificar este búfer en c, sin copiar.

Estos son los problemas que he encontrado. Tal vez hay más. Pero en general, el rendimiento no es tan diferente entre jna y jni, así que donde sea que puedas usar JNA, úsalo.

EDITAR

Esta respuesta parece ser bastante popular. Así que aquí están algunas adiciones:

  1. Si necesita asignar C++ o COM, hay una biblioteca por Oliver Chafic, creador de JNAerator, llamado BridJ. Todavía es una biblioteca joven, pero tiene muchas características interesantes:
    • dinámico de interoperabilidad C/C++/COM: llamar a los métodos C++, cree ++ objetos C (y la subclase C++ clases de Java!)
    • asignaciones de tipo directa, con buen uso de los medicamentos genéricos (incluyendo modelo mucho más agradable para los punteros) de apoyo
    • completa JNAerator
    • funciona en Windows, Linux, MacOS X, Solaris, Android
  2. en cuanto a la copia de la memoria, creo que JNA apoya ByteBuffers directos , entonces la memoria c opying puede ser evitado.

Creo 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 rendimiento es notable.

+6

No estoy de acuerdo, JNA tiene mucha sobrecarga. Aunque vale la pena utilizar su conveniencia en el código –

+3

, que no es crítico para el tiempo, aconsejo precaución antes de usar BridJ para un proyecto de Android, para citar directamente de su [página de descarga] (https://code.google.com/p/bridj/wiki/Descargar # Android): "BridJ funciona parcialmente en Android/arm emulators (con el SDK), y __probablemente incluso en dispositivos reales (no probados) .__" – kizzx2

+0

bueno, estoy usando jna y como denis dije que llamo a un método que me devuelve un gran buffer de bytes de datos de una imagen de un dispositivo usb. Pero en mi caso, el método depende de un dispositivo de cámara externo, de modo que hasta que alguien no capture la imagen, debe esperar dentro de ese dll. genéricamente esto funciona bien, pero en muchas otras computadoras que no sean las mías, automáticamente regresa del método tomando datos en blanco sin esperar siquiera un segundo. He enfrentado este problema en computadoras antiguas y la que tiene usb 3.0. Entonces, ¿es un problema jna? – Jony

5

No es una respuesta directa y no tengo experiencia con JNA pero, cuando miro el Projects Using JNA y veo nombres como SVNKit, IntelliJ IDEA, NetBeans IDE, etc., tiendo a pensar 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.

8
  1. Estás escribiendo el código hace unos años antes de que existiera JNA o que apuntan a un pre 1.4 JRE.
  2. El código con el que está trabajando no está en una DLL \ SO.
  3. Está trabajando en un código que no es compatible con LGPL.

Eso es solo lo que puedo pensar en mi cabeza, aunque tampoco soy un gran usuario de ninguno de los dos. También parece que podrías evitar JNA si querías una interfaz mejor que la que proporcionan, pero podrías codificarla en java.

+9

No estoy de acuerdo con 2: la conversión de lib estática a lib dinámica es fácil. Vea mi pregunta abput it http://stackoverflow.com/questions/845183/convert-static-windows-library-to-dll –

+1

Comenzando con JNA 4.0, JNA tiene licencia dual bajo LGPL 2.1 y Apache License 2.0, y que usted elige depende de usted – sbarber2

28

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 tipo se implementa en el lado nativo del borde Java/native, mientras que con JNA, la conversión de tipo se implementa en Java. Si ya te sientes bastante cómodo con la programación en C y tienes que implementar algún código nativo, supongo que JNI no parecerá demasiado complejo. Si usted es un programador de Java y solo necesita invocar una biblioteca nativa de un tercero, usar JNA es probablemente la ruta más fácil para evitar los problemas quizás no tan obvios con JNI.

Aunque nunca comparé diferencias, lo haría por el diseño, al menos supongo que la conversión de tipo con JNA en algunas situaciones funcionará peor que con JNI. Por ejemplo, al pasar matrices, JNA las convertirá de Java a nativo al comienzo de cada llamada de función y volverá al final de la llamada de función. Con JNI, puede controlarse a sí mismo cuando se genera una "vista" nativa de la matriz, creando solo una vista de una parte de la matriz, manteniendo la vista en varias llamadas de funciones y al final liberar la vista y decidir si desea para mantener los cambios (posiblemente requiriendo copiar los datos) o descartar los cambios (no se requiere copia). Sé que puede usar una matriz nativa en llamadas a funciones con JNA utilizando la clase de memoria, pero esto también requerirá una copia de memoria, que puede ser innecesaria con JNI. La diferencia puede no ser relevante, pero si su objetivo original es aumentar el rendimiento de la aplicación mediante la implementación de partes de la misma en código nativo, el uso de una tecnología de puente de peor rendimiento no parece ser la opción más obvia.

1

A menos que me falta algo, ¿no es la principal diferencia entre JNA y JNI que con JNA no se puede llamar al código Java del código nativo (C)?

+6

Puede. Con una clase de devolución de llamada que corresponde a un puntero de función en el lado C. void register_callback (void (*) (const int)); se correlacionaría con el vacío nativo estático register_callback (MyCallback arg1); donde MyCallback es una interfaz que extiende com.sun.jna.Callback con un único método void apply (int value); –

+0

@Augusto esto también puede ser un comentario – swiftBoy

7

Por cierto, en uno de nuestros proyectos, mantuvimos una huella de JNI muy pequeña. Usamos memorias intermedias de protocolo para representar nuestros objetos de dominio y, por lo tanto, teníamos solo una función nativa para unir Java y C (entonces, por supuesto, esa función C llamaría a muchas otras funciones).

+4

Por lo tanto, en lugar de una llamada a un método, tenemos mensajes que pasan. He invertido bastante tiempo en JNI y JNA y también en BridJ, pero muy pronto todos se vuelven un poco atemorizantes. –

2

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 mayor rendimiento que JNA (aproximadamente 40 veces). Tal vez hay un truco para un mejor rendimiento en JNA, pero es muy lento para un simple ejemplo.

3

Si desea un rendimiento JNI pero se siente intimidado por su complejidad, 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 p. hacer llamadas de C++ a Java usando la sintaxis estándar de Java. Por ejemplo, aquí te mostramos cómo imprimir una cadena C a la salida estándar de Java:

native "C++" void printHello() { 
    const char* helloWorld = "Hello, World!"; 
    `System.out.println(#$(helloWorld));` 
} 

JANET luego se traduce el Java backtick embebido en el JNI apropiada llama.

1

De hecho, hice algunos benchmarks simples con JNI y JNA.

Como ya han señalado otros, JNA es por conveniencia. No necesita compilar o escribir código nativo cuando usa JNA. El cargador de bibliotecas nativo de JNA también es uno de los mejores/más fáciles de usar que he visto en mi vida. Lamentablemente, no se puede usar para JNI, parece. (Es por eso que escribí an alternative for System.loadLibrary() que usa la convención de ruta de acceso de JNA y admite carga sin problemas desde classpath (es decir, jar).)

El rendimiento de JNA sin embargo, puede ser mucho peor que el de JNI. Hice una prueba muy simple que llamó a una simple función de incremento entero nativo "return arg + 1;". Los puntos de referencia hechos 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" donde la función nativa resume una matriz entera de 4 valores aún mostraba 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 liberó nuevamente durante cada operación de suma.

El código y los resultados de las pruebas se pueden encontrar at github.

Cuestiones relacionadas