Consultor tecnológico de desarrollo de proyectos informáticos.
Ingeniero en Informática
Fecha de publicación del tutorial: 2006-02-14
Indice
1.Introduccion.......................................................................................................................................... 2
2.Declaracion de la clase nativa................................................................................................................ 3
3.Implementacion de los metodos de la DLL.............................................................................................. 4
3.1.Nomenclatura de los metodos........................................................................................................ 4
3.2.Convencion de llamada.................................................................................................................. 4
3.3.Manipulacion de objetos................................................................................................................. 4
3.4.Manejo de excepciones................................................................................................................. 4
3.4.1.Capturar excepciones lanzadas por la parte Java...................................................................... 4
3.4.2.Lanzar excepciones a la parte Java......................................................................................... 5
4.Ejemplo de implementación................................................................................................................... 6
4.1.Parte Java..................................................................................................................................... 6
4.2.Parte C++..................................................................................................................................... 7
4.3.Paso a paso................................................................................................................................. 8
5.Apendice............................................................................................................................................ 11
5.1.Secuencias de escape en los nombres de funcion.......................................................................... 11
5.2.Signaturas de funcion................................................................................................................... 11
5.3.Equivalencia de tipos primitivos..................................................................................................... 12
5.4.Equivalencia de tipos compuestos................................................................................................. 12
1. Introduccion
El objetivo de este tutorial es mostrar como se puede implementar una clase Java con metodos nativos escritos en C++ y contenidos en una DLL de Windows. Se puede extrapolar facilmente a Unix, generando librerias compartidas de Unix (normalmente ficheros con extension .so) en vez de DLLs.
El mecanismo JNI de Java viene incluido con cualquier JDK de Sun y consiste en unos ficheros .h que hay en el directorio include del JDK. Dichos ficheros .h definen la interfaz entre nuestra DLL nativa y la JVM.
Para crear una clase Java con metodos nativos solo hay que marcar los metodos de la clase con la palabra native y, despues, implementarlos en la DLL siguiendo una convenciones sencillas.
A la hora de ejecutar solo es necesario que nuestra clase Java carque la DLL con los metodos nativos mediante una llamada a System.loadLibrary. La DLL se buscara en el PATH de Windows y en todos los directorios apuntados por la propiedad del sistema Java llamada java.library.path.
Este tutorial tiene dos capitulos genericos (2 y 3) que explican como se implementan los metodos nativos, un capitulo 4 con un ejemplo, y un apendice con referencias extraidas de la documentacion del JDK 5.0 de Sun.
2. Declaracion de la clase nativa
Lo primero que tenemos que hacer es crear un fichero .java con la definicion de la clase como si fuesemos a escribir una clase Java normal. Todos los metodos que esten implementados en la DLL nativa deben llevar el prefijo native.
Además, hay que definir el constructor estatico de la clase y, dentro de el, llamar a System.loadLibrary para cargar la DLL que implementara los metodos nativos.
Ejemplo:
class Cls
// Metodo implementado nativamente
native double f(int i, String s);
// Inicializacion de la clase
static
// Cargar la DLL que implementa la clase
System.loadLibrary(“pkg_autentia”);
La DLL se puede llamar como queramos, no hay ninguna restriccion sobre el nombre. Lo importante son los nombres de los metodos.
Este .java hay que compilarlo y nos dara un .class que, al usarlo, cargara la DLL y llamara a los metodos nativos. Si no se carga la DLL, la JVM lanzara una excepcion UnsatisfiedLinkError al llamar a los metodos nativos.
3. Implementacion de los metodos de la DLL
3.1. Nomenclatura de los metodos
Los nombres de los metodos deben seguir una convención predefinida:
● Metodos sobrecargados: Java_<clase cualificada>_<nombre del metodo>__<signatura de los argumentos>
● Metodos unicos: Java_<clase cualificada>_<nombre del metodo>__<signatura de los argumentos>
Existen ciertas secuencias de escape para los nombres de los metodos que estan listadas en el apéndice. Asimismo, se puede consultar en el mismo apéndice una explicacion de como se compone la signatura de los argumentos. La signatura describe los argumentos que recibe el metodo de forma inequivoca y es necesaria puesto que, debido a la sobrecarga, metodos con el mismo nombre pueden recibir distintos argumentos.
3.2. Convencion de llamada
La convencion de llamada de los argumentos implementados nativamente es la siguiente:
● Para Win32 se usa la convencion de llamada __stdcall (PASCAL).
● Para Unix se usa la convencion de llamada __cdecl (C).
● Parametros:
Un puntero a objeto JNIEnv, que se usa para comunicarse con la JVM
Según el tipo
de metodo:
■
Estatico: una
referencia a la clase
■
No estatico:
una referencia al objeto (this)
Resto de
argumentos del metodo
3.3.
Manipulacion de objetos
Todos los objetos
Java manipulados en la implementacion nativa se deben acceder a traves del
puntero al objeto JNIEnv. Existen unas equivalencias entre tipos Java y tipos
nativos que se pueden consultar en el apendice.
Se pueden consultar
las funciones existentes en el objeto JNIEnv en la documentacion del JNI o en el fichero jni.h. Todas las funciones son bastante intuitivas, en cuanto al
nombre y a los parametros.
Es importante saber
que todo metodo o campo de objeto se accede a traves del objeto JNIEnv despues de haber obtenido su ID. Para obtener el ID de
un metodo o un campo hay que emplear funciones tambien en el JNIEnv. El mecanismo de ID es necesario puesto que puede haber
metodos sobrecargados, campos heredados, etc, y porque, ademas, todo el acceso
a objetos Java debe ser indirecto para no interferir con el recolector de
basura.
3.4.
Manejo de excepciones
3.4.1. Capturar excepciones lanzadas por la parte Java
Cuando se llama a
funciones del JNIEnv o de otras clases Java, se pueden producir excepciones.
Para determinar si ha habido alguna excepcion se llama a la funcion ExceptionOccurred. Si hay alguna excepcion pendiente, se debe llamar a ExceptionClear para desactivarla (es como si se hubiese hecho un catch) o
devolver el control a la parte Java (retornar del metodo) para que la excepcion
se propague a la parte Java.
3.4.2. Lanzar excepciones a la parte Java
Las excepciones que
se quieran lanzar hacia la parte Java, se deben propagar con los metodos Throw o ThrowNew del JNIEnv.
4.
Ejemplo de implementación
A continuacion se
describe un ejemplo de implementacion de un metodo nativo estatico y de otro no
estatico. Ademas, en el metodo estatico se muestra como lanzar una excepcion.
4.1.
Parte Java
Las clase Java
utilizadas tienen el siguiente codigo:
autentia/Cls.java
package autentia;
public class Cls
{
native public int
noEstatico(int i);
native public
static int estatico(int i) throws TestException;
int campo;
static
{
String lib
= "tutorial-jni";
System.err.println("cargando
libreria "+lib );
System.loadLibrary(
lib );
}
public static
void main( String[] args )
{
Cls t = new
Cls();
int i;
i =
t.noEstatico( 17 );
System.err.println("t.noEstatico(17)="+i);
System.err.println("t.campo="+t.campo);
try
{
i =
Cls.estatico( 26 );
System.err.println("Cls.estatico(26)="+i);
i =
Cls.estatico( -1 );
System.err.println("Cls.estatico(-1)="+i);
}
catch(
TestException e )
{
System.err.println("Cls.estatico(-1)
catch "+e);
}
}
}
autentia/TestException.java
package autentia;
public class
TestException extends Exception
{
private static final long serialVersionUID = 1L;
private int n;
public TestException( String msg, int n )
{
super(msg);
this.n = n;
}
public String toString()
{
return
"TestException["+getMessage()+","+n+"]";
}
}
4.2.
Parte C++
Los ficheros de la
DLL, escrita en C++, tienen el siguiente codigo:
tutorial-jni.h
#include <jni.h>
// native public int
no_estatico(int i);
extern "C" __declspec(dllexport)
jint Java_autentia_Cls_noEstatico__I( JNIEnv* env, jobject obj, jint i );
// native public
static int estatico(int i) throws TestException;
extern "C" __declspec(dllexport)
jint Java_autentia_Cls_estatico__I( JNIEnv* env, jobject obj, jint i );
#include "tutorial-jni.h"
extern "C" __declspec(dllexport) jint Java_autentia_Cls_noEstatico__I(
JNIEnv* env, jobject obj, jint i )
{
// cambiar el valor del atributo "campo" del
objeto
jclass
clazz = env->GetObjectClass(obj);
jfieldID
idCampo = env->GetFieldID( clazz, "campo", "I" );
env->SetIntField(
obj, idCampo, i );
// devolver el valor pasado como parametro
return i;
}
extern "C" __declspec(dllexport) jint Java_autentia_Cls_estatico__I(
JNIEnv* env, jobject obj, jint i )
{
// si nos pasan -1 lanzamos una excepcion
if( i==-1 )
{
jclass
clazz = env->FindClass("autentia/TestException");
jmethodID
idCtor = env->GetMethodID( clazz, "<init>",
"(Ljava/lang/String;I)V" );
jstring
msg = env->NewStringUTF( "mensaje de la excepcion" );
jobject
ex = env->NewObject( clazz, idCtor, msg, -1 );
env->Throw(
(jthrowable)ex );
}
// devolver el valor pasado como parametro
return i;
}
4.3.
Paso a paso
Lo primero que
haremos es escribir las clase Java con nuestro editor de ficheros favorito,
guardarlas y compilarlas. Esto nos dara dos ficheros .class que nos permiten
probar el ejemplo. Si ejecutamos la clase Cls, se producira el siguiente error:

Este error se debe a
que la JVM no encuentra los metodos nativos. Logico, puesto que aun no hemos
creado la DLL. Para crear la DLL, abrimos un nuevo projecto DLL en el Visual
Studio de Microsoft o en el compilador de C que mas nos guste.
Añadimos al proyecto
los ficheros tutorial-jni.h y tutorial-jni.cpp descritos mas arriba y modificamos los directorios de
inclusion para que incluyan los directorios del JDK que contienen los ficheros
.h del JNI. Dichos directorios son include e include/win32 dentro del directorio de instalacion del JDK. No son
necesarias librerias puesto que todo lo necesario esta en los ficheros .h.
![]()

Compilamos la DLL y
echamos el fichero resultante en el directorio base del proyecto Java:




A continuacion ejecutamos el proyecto de
nuevo, con lo que obtenemos el siguiente resultado, que demuestra las llamadas
al codigo C++ desde la clase Java:

5.
Apendice
Tablas y notas
extraidas de la documentacion de JNI del JDK 5.0 de Sun.
5.1.
Secuencias de escape en
los nombres de funcion
|
a Unicode character XXXX. |
|
5.2. Signaturas de funcion
long f (int n, String s, int[] arr);
has the following type signature:
(ILjava/lang/String;[I)J
5.3.
Equivalencia de tipos
primitivos
The following definition is provided for convenience.
The jsize integer type is used to describe cardinal indices and sizes:
5.4. Equivalencia de tipos compuestos
The JNI includes a number of reference types that correspond to different kinds of Java objects. JNI reference types are organized in the hierarchy shown in Figure 3-1.
|
jobject |
java.lang.Object |
||
|
|
jclass |
java.lang.Class |
|
|
|
jstring |
java.lang.String |
|
|
|
jarray |
arrays nativos de Java |
|
|
|
|
jobjectArray |
Java.lang.Object [] |
|
|
|
jbooleanArray |
boolean [] |
|
|
|
jbyteArray |
byte [] |
|
|
|
jcharArray |
char [] |
|
|
|
jshortArray |
short [] |
|
|
|
jintArray |
int [] |
|
|
|
jlongArray |
long [] |
|
|
|
jfloatArray |
float [] |
|
|
|
jdoubleArray |
double [] |
|
|
jthrowable |
java.lang.Throwable |
|
Figure 3-1 Reference Type Hierarchy
In C, all other JNI reference types are defined to be the same as jobject. For example:
In C++, JNI introduces a set of dummy classes to enforce the subtyping relationship. For example:
class _jclass : public _jobject {};
The jvalue union type is used as the element type in argument arrays. It is declared as follows:
A continuación puedes evaluarlo:
Fecha publicación: 2007-08-09-07:57:20
Autor:
Fecha publicación: 2006-06-17-06:06:09
Autor:
Fecha publicación: 2006-04-02-01:13:41
Autor:
Esta obra está licenciada bajo licencia Creative Commons de
Reconocimiento-No comercial-Sin obras derivadas 2.5











