20 julio, 2009

Acceso a Métodos Nativos (JNI vs. JNA)

.

Este capítulo está siendo desarrollado, puede contener errores.

Java provee la posibilidad de acceder a funciones desarrolladas en C a través de lo que se llama "métodos nativos". Actualmente existen dos tecnologías a través de las cuales podemos invocar funciones C desde nuestros programas Java. Estas son:
  • JNI - "Java Native Interface" (la tecnología tradicional), y
  • JNA - "Java Native Access"
Si bien ambas permiten acceder a funciones C desde Java, la metodología de trabajo es muy diferente. JNI es extremadamente más complejo que JNA.

En este capítulo desarrollaremos algunos ejemplos en ambas tecnologías con los objetivos de comprender como se utilizan y de poderlas comparar.


HolaMundo - JNI

Usar JNI es algo engorroso. A continuación voy a enumerar los pasos que hay que realizar para invocar a una función C que imprima "Hola Mundo" en la consola.

Si el lector considera que este ejemplo es demasiado complicado o se le complica seguirlo hasta el final le recomiendo que vaya directamente a ver la implementación desarrollada con JNA expuesta más abajo.

1 - Definir una clase Java con los métodos nativos

CLib.java (clase con métodos nativos)
   1:
2:package demo.jni;
3:
4:public class CLib
5:{
6: // los metodos nativos no se desarrollan
7: public native void holaMundo();
8:}
9:


2 - Compilar el .java anterior y luego correr el comando javah para generar el .h con los headers C que se corresponden con los métodos nativos definidos en la clase.

javah -jni demo.jni.CLib

Debemos tener en cuenta que para ejecutar este comando desde la línea de comandos tenemos que tener seteado el PATH a %JAVA_HOME%\bin y debemos estar posicionados en el directorio padre del paquete demo o bien debemos tener seteado correctamente el CLASSPATH.

El comando javah debe generar el siguiente archivo:

demo_jni_CLib.h
   1:/* DO NOT EDIT THIS FILE - it is machine generated */
2:#include <jni.h>
3:/* Header for class demo_jni_CLib */
4:
5:#ifndef _Included_demo_jni_CLib
6:#define _Included_demo_jni_CLib
7:#ifdef __cplusplus
8:extern "C" {
9:#endif
10:/*
11: * Class: demo_jni_CLib
12: * Method: holaMundo
13: * Signature: ()V
14: */
15:JNIEXPORT void JNICALL Java_demo_jni_CLib_holaMundo
16: (JNIEnv *, jobject);
17:
18:#ifdef __cplusplus
19:}
20:#endif
21:#endif
22:


3 - Programar en C la implementación del método nativo holaMundo utilizando el header que obtuvimos luego de correr el comando javah.

demoJNI.c
   1:
2:#include<stdio.h>
3:#include<jni.h>
4:#include "demo_jni_CLib.h"
5:
6:JNIEXPORT void JNICALL Java_demo_jni_CLib_holaMundo (JNIEnv *env, jobject obj)
7:{
8: printf("Hola Mundo\n");
9:}
10:


4 - Compilar el código C y generar una DLL. Si usamos MinWG el comando será el siguiente:

gcc -c -I%JAVA_HOME\include -I%JAVA_HOME%\include\win32 demoJNI.c
gcc -shared -o demoJNI.dll demoJNI.o

Esto generara el archivo demoJNI.dll.


5 - Escribir un programa que instancie la clase CLib e invoque al método holaMundo.

Demo.java
   1:
2:package demo.jni;
3:
4:public class Demo
5:{
6: public static void main(String[] args)
7: {
8: System.loadLibrary("demoJNI");
9: CLib clib = new CLib();
10: clib.holaMundo();
11: }
12:}
13:

Más información en: http://java.sun.com/docs/books/jni/html/start.html

Hola Mundo - JNA

El enfoque que provee JNA es mucho más intuitivo, simple y directo. Solo necesitamos descargar el archivo jna.jar.

1 - En JNA comenzamos por escribir el código C sin condicionarlo con ningún header extraño.

demoJNA.c
   1:
2:#include <stdio.h>
3:void holaMundo()
4:{
5: printf("Hola Mundo\n");
6:}
7:


2 - El siguiente paso es crear una interface Java que defina el método holaMundo que "wrappea" a la función escrita en C.

CLib.java
   1:
2:package demo.jna;
3:
4:import com.sun.jna.Library;
5:
6:public interface CLib extends Library
7:{
8: public void holaMundo();
9:}
10:


3 - Por último podemos escribir el programa principal que invoca al método holaMundo de la interface CLib.

Demo.java
   1:
2:package demo.jna;
3:
4:import com.sun.jna.Native;
5:
6:public class Demo
7:{
8: public static void main(String[] args)
9: {
10: CLib clib= (CLib) Native.loadLibrary("demoJNA", CLib.class);
11: clib.holaMundo();
12: }
13:}
14:

Más información en: https://jna.dev.java.net/












.

6 comentarios:

Anónimo dijo...

Una curiosidad, ¿Qué herramienta utilizas para generar el código Java con syntax highlight que pones en tus mensajes?
Gracias.

Anónimo dijo...

Hola amigo, esta muy padre tu post :P bueno mi pregunta es la siguiente:

¿con JNA tambien puedo hacer uso tanto de archivos *.c como TAMBIEN DE librerias dinamicas (.dll)?

Esto lo pregunto por que vi que en tu post usaste JNA solo para implementar un archivo *.c

bug_ober dijo...

Muy bueno

Unknown dijo...

gracias por el aporte XD!
sirvio de mucho ...

por si acaso aclaro q para JNA tmb se crea la dll y se pega en c:/windows/system32

y el programa es MinGW
entras con cmd a
C:\MinGW\bin
y pegas tu archivo *.c
y ejecutas los comandos q dice alli

gcc -c -I%JAVA_HOME\include -I%JAVA_HOME%\include\win32 archivo.c

//genera archivo.o
gcc -shared -o archivo1.dll archivo.o

//genera archivo1.dll

y claro Eclipse tiene a MinGW para convertir un archi.c a dll

pd:compartir experiencias es lo q hace a la web una red

Anónimo dijo...

mmmmchale no se que pasa que no me sale el ejemplo de jna!!!

el error lo tengo en:

Native.loadLibrary("demoJNA", CLib.class);

Digo no es un error de sintaxis ni nada solamente que cuando lo corro me dice que demoJNA no existe, sabes no se si lo copie mal o no se pero quiza es por que loanlirary necesita hacer una referencia a una libreria dinamica .dll y no a un codigo sin compilar de c++ como es el caso de demoJNA.c

Anónimo dijo...

Buenas que tal? Muy interesante este articulo, pero tengo un par de preguntas.

JNA es mucho mas sencillo que JNI, pero funciona con C++? habria forma de hacerlo funcionar, de manera que siga quedando mas sencillo de utilizar que JNI?

Desde ya muchas gracias!