En el diseño de software , la interfaz nativa de Java ( JNI ) es una interfaz de la función externa de programación marco que permite a Java código que se ejecuta en una máquina virtual Java (JVM) para llamar y ser llamado por [1] aplicaciones nativas (programas específicos de un hardware y operativo plataforma del sistema ) y bibliotecas escritas en otros lenguajes como C , C ++ y ensamblador .
Objetivos
JNI permite a los programadores escribir métodos nativos para manejar situaciones en las que una aplicación no se puede escribir completamente en el lenguaje de programación Java, por ejemplo, cuando la biblioteca de clases estándar de Java no es compatible con las características específicas de la plataforma o la biblioteca de programas. También se utiliza para modificar una aplicación existente (escrita en otro lenguaje de programación) para que sea accesible a las aplicaciones Java. Muchas de las clases de biblioteca estándar dependen de JNI para proporcionar funcionalidad al desarrollador y al usuario, por ejemplo, E / S de archivos y capacidades de sonido. La inclusión de implementaciones de API sensibles al rendimiento y a la plataforma en la biblioteca estándar permite que todas las aplicaciones Java accedan a esta funcionalidad de una manera segura e independiente de la plataforma.
El marco JNI permite que un método nativo use objetos Java de la misma manera que el código Java usa estos objetos. Un método nativo puede crear objetos Java y luego inspeccionarlos y usarlos para realizar sus tareas. Un método nativo también puede inspeccionar y utilizar objetos creados por código de aplicación Java.
Solo las aplicaciones y los subprogramas firmados pueden invocar JNI.
Una aplicación que depende de JNI pierde la portabilidad de la plataforma que ofrece Java (una solución parcial es escribir una implementación separada del código JNI para cada plataforma y hacer que Java detecte el sistema operativo y cargue el correcto en tiempo de ejecución).
El código nativo no solo puede interactuar con Java, sino que también puede basarse en Java Canvas
, lo que es posible con la interfaz nativa de Java AWT . El proceso es casi el mismo, con solo algunos cambios. La interfaz nativa de Java AWT solo está disponible desde J2SE 1.3.
JNI también permite el acceso directo al código ensamblador , sin siquiera pasar por un puente C. [2] El acceso a las aplicaciones Java desde el ensamblaje es posible de la misma manera. [3]
Diseño
En el marco JNI, las funciones nativas se implementan en archivos .co .cpp separados. (C ++ proporciona una interfaz un poco más simple con JNI). Cuando la JVM invoca la función, pasa un JNIEnv
puntero, un jobject
puntero y cualquier argumento de Java declarado por el método Java. Por ejemplo, lo siguiente convierte una cadena Java en una cadena nativa:
extern "C" JNIEXPORT void JNICALL Java_ClassName_MethodName ( JNIEnv * env , jobject obj , jstring javaString ) { const char * nativeString = env -> GetStringUTFChars ( javaString , 0 ); // Haz algo con nativeString env -> ReleaseStringUTFChars ( javaString , nativeString ); }
El env
puntero es una estructura que contiene la interfaz de la JVM. Incluye todas las funciones necesarias para interactuar con la JVM y trabajar con objetos Java. Las funciones de JNI de ejemplo son convertir matrices nativas a / desde matrices Java, convertir cadenas nativas a / desde cadenas Java, instanciar objetos, lanzar excepciones, etc. Básicamente, cualquier cosa que el código Java pueda hacer se puede hacer usando JNIEnv
, aunque con considerablemente menos facilidad.
El argumento obj
es una referencia al objeto Java dentro del cual se ha declarado este método nativo.
Los tipos de datos nativos se pueden asignar a / desde tipos de datos Java. Para tipos compuestos como objetos, matrices y cadenas, el código nativo debe convertir explícitamente los datos llamando a métodos en el JNIEnv
.
Se pasa un puntero de entorno JNI ( JNIEnv * ) como argumento para cada función nativa mapeada a un método Java, lo que permite la interacción con el entorno JNI dentro del método nativo. Este puntero de interfaz JNI se puede almacenar, pero sigue siendo válido solo en el subproceso actual. Otros hilos deben llamar primero AttachCurrentThread () para conectarse a la VM y obtener un puntero de interfaz JNI. Una vez adjunto, un hilo nativo funciona como un hilo Java normal que se ejecuta dentro de un método nativo. El hilo nativo permanece adjunto a la VM hasta que llama DetachCurrentThread () para separarse. [4]
El marco JNI no proporciona ninguna recolección automática de basura para los recursos de memoria que no son JVM asignados por el código que se ejecuta en el lado nativo. En consecuencia, el código del lado nativo (como el lenguaje ensamblador) asume la responsabilidad de liberar explícitamente los recursos de memoria que adquiere el código nativo.
En las plataformas Linux y Solaris, si el código nativo se registra como manejador de señales, podría interceptar señales destinadas a la JVM. Se puede utilizar una cadena de responsabilidad para permitir que el código nativo interactúe mejor con la JVM. En las plataformas Windows, el Manejo de excepciones estructurado (SEH) se puede emplear para envolver el código nativo en los bloques SEH try / catch para capturar interrupciones de software generadas por la máquina (CPU / FPU) (como violaciones de acceso de puntero NULL y operaciones de división por cero). ), y para manejar estas situaciones antes de que la interrupción se propague de nuevo a la JVM (es decir, el código del lado de Java), lo que probablemente resultará en una excepción no controlada. [ investigación original? ]
La codificación utilizada para las funciones NewStringUTF, GetStringUTFLength, GetStringUTFChars, ReleaseStringUTFChars y GetStringUTFRegion es "UTF-8 modificado", [5] que no es UTF-8 válido para todas las entradas, pero en realidad es una codificación diferente. El carácter nulo (U + 0000) y los puntos de código que no están en el plano multilingüe básico (mayor o igual a U + 10000, es decir, los representados como pares sustitutos en UTF-16) se codifican de forma diferente en UTF-8 modificado. Muchos programas utilizan estas funciones incorrectamente y tratan las cadenas UTF-8 devueltas o pasadas a las funciones como cadenas UTF-8 estándar en lugar de cadenas UTF-8 modificadas. Los programas deben usar las funciones NewString, GetStringLength, GetStringChars, ReleaseStringChars, GetStringRegion, GetStringCritical y ReleaseStringCritical, que usan codificación UTF-16LE en arquitecturas little-endian y UTF-16BE en arquitecturas big-endian, y luego usan un UTF- 16 8 rutina de conversión. [ investigación original? ]
Tipos de mapeo
La siguiente tabla muestra el mapeo de tipos entre Java (JNI) y código nativo.
Tipo C | Tipo de lenguaje Java | Descripción | Firma de tipo |
---|---|---|---|
carácter sin firmar uint8_t | jboolean | 8 bits sin firmar | Z |
carácter firmado int8_t | jbyte | 8 bits firmados | B |
corto sin firmar uint16_t | jchar | 16 bits sin firmar | C |
corto int16_t | jshort | 16 bits firmados | S |
int int32_t | jint | 32 bits firmados | I |
largo largo | jlong | 64 bits firmados | J |
flotador | jfloat | 32 bits | F |
doble | jdouble | 64 bits | D |
vacío | V |
Además, la firma "L fully-qualified-class ;"
significaría la clase especificada de forma única por ese nombre; por ejemplo, la firma se "Ljava/lang/String;"
refiere a la clase java.lang.String
. Además, el prefijo [
de la firma hace que la matriz sea de ese tipo; por ejemplo, [I
significa el tipo de matriz int. Finalmente, una void
firma usa el V
código.
Estos tipos son intercambiables. Se puede usar jint
donde se usa normalmente int
y viceversa, sin necesidad de encasillamiento . Sin embargo, el mapeo entre las cadenas y matrices de Java a las cadenas y matrices nativas es diferente. Si jstring
se usa a donde char *
estaría, el código podría bloquear la JVM. [ investigación original? ]
Actuación
JNI incurre en una considerable pérdida de rendimiento y gastos generales en determinadas circunstancias: [6]
- Las llamadas a funciones a los métodos JNI son costosas, especialmente cuando se llama a un método repetidamente.
- Los métodos nativos no están integrados por la JVM, ni el método puede ser compilado JIT , ya que el método ya está compilado.
- Una matriz de Java se puede copiar para acceder en código nativo y luego volver a copiar. El costo puede ser lineal en el tamaño de la matriz.
- Si al método se le pasa un objeto, o necesita hacer una devolución de llamada, entonces el método nativo probablemente hará sus propias llamadas a la JVM. Acceder a los campos, métodos y tipos de Java desde el código nativo requiere algo similar a la reflexión . Las firmas se especifican en cadenas y se consultan desde la JVM. Esto es lento y propenso a errores.
- Las cadenas de Java son objetos, tienen longitud y están codificadas. Acceder o crear una cadena puede requerir una copia O (n).
Alternativas
La implementación propietaria de Microsoft de una máquina virtual Java ( Visual J ++ ) tenía un mecanismo similar para llamar al código nativo desde Java, llamado Raw Native Interface ( RNI ). Además, tenía una manera fácil de llamar al código nativo existente que no conocía Java, como (pero no limitado a) la API de Windows, llamada J / Direct . Sin embargo, tras el litigio Sun-Microsoft sobre esta implementación, Visual J ++ ya no se mantiene.
RNI fue menos torpe de usar que JNI, porque no se necesitaba contabilidad con un puntero de entorno Java. En cambio, se puede acceder directamente a todos los objetos de Java. Para facilitar esto, se utilizó una herramienta que generó archivos de encabezado a partir de clases de Java. De manera similar, J / Direct fue más fácil de usar que usar la biblioteca nativa intermedia necesaria y JNI, aunque en la actualidad JNA es una alternativa. [ investigación original? ]
Ver también
Referencias
- ^ "Descripción general de la interfaz nativa de Java" . La guía y la especificación del programador de la interfaz nativa de Java . Consultado el 27 de diciembre de 2018 .
- ^ "Invocación de programas en lenguaje ensamblador desde Java" . Java.net. 2006-10-19. Archivado desde el original el 30 de marzo de 2008 . Consultado el 6 de octubre de 2007 .
- ^ "Ejecutar aplicaciones Java desde programas en lenguaje ensamblador" . Java.net. 2006-10-19. Archivado desde el original el 11 de octubre de 2007 . Consultado el 4 de octubre de 2007 .
- ^ La API de invocación. Sun Microsystems. https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocation.html
- ^ "Tipos JNI y estructuras de datos" .
- ^ "Java - ¿Qué hace que las llamadas JNI sean lentas? - Stack Overflow" .
Bibliografía
refbegin citar libro primero = Rob último = Gordon fecha = marzo de 1998 title = Essential Jni: Interfaz nativa de Java editor = Prentice Hall edición = 1ra páginas = 498 isbn = 0-13-679895-0 url = http://www.informit.com/store/product.aspx?isbn=9780136798958 citar libro primero = Sheng último = Liang fecha = 20 de junio de 1999 title = Interfaz nativa Java (TM): Guía y especificaciones del programador editor = Prentice Hall edición = 1ra páginas = 320 isbn = 0-201-32577-2 url = http://www.informit.com/store/product.aspx?isbn=9780201325775
Reembolso Adorable.
enlaces externos
- Especificación de la API JNI 6.0 de Oracle
- Interfaz nativa de Java: Guía y especificaciones del programador
- JNI en XCode de Apple
- Manejo de excepciones en JNI
- Java Link (contenedor moderno de C ++ 17 para JNI)