Utilizar el Java Native Interface (JNI)

Esta p�gina ilustra c�mo llamar a m�todos Java desde m�todos nativos. Nuestro programa de ejemplo, Callbacks.java, llama a un m�todo nativo. El m�todo nativo hace una nueva llamada al m�todo Java. Para hacer las cosas un poco m�s interesantes, el m�todo Java llama de nuevo (recursivamente) al m�todo nativo. Este proceso contin�a hasta que la recursi�n alcanza cinco niveles de profundidad, en ese momento el m�todo Java retorna sin hacer m�s llamadas al m�todo nativo. Para ayudarnos a ver esto, los dos m�todos imprimen una secuencia de trazado.

.�Llamar a un M�todo Java desde un M�todo Nativo

Para ver como el c�digo nativo llama al m�todo Java, enfoqu�monos en la implementaci�n de Callbacks_nativeMethod, que est� implementada en Callbacks.c. Este m�todo nativo contiene una llamada al m�todo java Callbacks.callback.

JNIEXPORT void JNICALL
Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)
{
  jclass cls = (*env)->GetObjectClass(env, obj);
  jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
  if (mid == 0)
    return;
  printf("In C, depth = %d, about to enter Java\n", depth);
  (*env)->CallVoidMethod(env, obj, mid, depth);
  printf("In C, depth = %d, back from Java\n", depth);
}

Se puede llamar a un m�todo de ejemplar (no est�tico) siguiendo estos tres pasos.

  • El m�todo nativo llama a la funci�n JNI GetObjectClass. GetObjectClass devuelve el objeto class Java al que pertenece el objeto.
  • Entonces el m�todo nativo llama a la funci�n JNI GetMethodID. GetMethodID realiza un b�squeda del m�todo Java dentro de la clase dada. La b�squeda est� basada tanto en el nombre del m�todo como en su firma. Si el m�todo no existe, GetMethodID devuelve cero (0). Un retorno inmediato desde el m�todo nativo en ese punto causa el lanzamiento de una excepci�n NoSuchMethodError en el c�digo Java.
  • Finalmente, el m�todo nativo llama a la funci�n JNI CallVoidMethod. CallVoidMethod llama a un m�todo de ejemplar que tiene el tipo de retorno como void. Se pasan el objeto, el ID del m�todo y los argumentos reales a CallVoidMethod.

.�Formar el Nombre del M�todo y su Firma

El JNI realiza un b�squeda simb�lica bas�ndose en el nombre y la firma del m�todo. Esto asegura que el mismo m�todo nativo seguir� funcionando despu�s de haber a�adido nuevos m�todos a la clase Java correspondiente.

El nombre del m�todo es el nombre del m�todo en formato UTF-8. Especifica el nombre del m�todo para un constructor de una clase encerrando la palabra init entre "< y >".

Observa que el JNI utiliza la firma del m�todo para denotar el tipo de retorno del m�todo Java. Esta firma (I)V, por ejemplo, denota un m�todo Java que toma un argumento del tipo int y tiene un tipo de retorno void. La forma general para una firma de m�todo es.

"(tipos-argumentos)tipo-retorno"

La siguiente tabla sumariza la codificaci�n Java para las firmas.

.�Tipos de Firmas de la VM de Java

Firma Tipo Java
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L clases-totalmente-cualificada ; clase-totalmente-cualificada
[ tipo tipo[]
( tipo-argumento ) tipo-retorno tipo de m�todo

Por ejemplo, el m�todo Prompt.getLine tiene la firma.

(Ljava/lang/String;)Ljava/lang/String;

Prompt.getLine tiene un par�metro, un objeto String, y el tipo del m�todo tambi�n es String.

El m�todo Callbacks.main tiene la firma.

([Ljava/lang/String;)V

La firma indica que el m�todo Callbacks.main toma un par�metro, un objeto String, y el tipo del m�todo es void.

Los tipos de arrays son indicados con corchete abierto ([) seguido por el tipo de los elementos del array.

.�Utilizar javap para Generar Firmas de M�todos

Para evitar los errores derivados de la firma de m�todos manual, se puede utilizar la herramienta javap para imprimir las firmas de m�todos. Por ejemplo, ejecutando.

javap -s -p Prompt

Se puede obtener la siguiente salida.

Compiled from Prompt.java
class Prompt extends java.lang.Object 
    /* ACC_SUPER bit set */
{
    private native getLine (Ljava/lang/String;)Ljava/lang/String;
    public static main ([Ljava/lang/String;)V
    <init> ()V
    static <clinit> ()V
}

La bandera "-s" informa a javap para que saque las firmas en vez de los tipos Java. La bandera "-p" instruye a javap para que incluya los miembros privados.

.�Llamar a M�todos Java utilizando los IDs

Cuando se llama a un m�todo en el JNI, se le pasa un ID del m�todo a la funci�n real de llamada. Obtener un ID de un m�todo es una operaci�n que consume muchos recursos . Como se obtiene el ID del m�todo de forma separada de la llamada al m�todo, s�lo se necesita realizar esta operaci�n una vez. As�, es posible, obtener primero el ID del m�todo y luego utilizarlo las veces necesarias para invocar al mismo m�todo.

Es importante tener en mente que un ID de m�todo s�lo es v�lido mientras que no se descargue la clase de la se deriva. Una vez que la clase se ha descargado, el ID del m�todo no es v�lido. Como resultado, si se quiere guardar el ID del m�todo, debemos asegurarnos de mantener viva a una referencia a la clase Java de la que se deriva el ID del m�todo. Mientras exista la referencia a la clase Java (el valor jclass), el c�digo nativo mantendr� una referencia viva a la clase. La p�gina Referencias Locales y Globales explica como mantener viva una referencia incluso despu�s de que el m�todo nativo retorne y el valor de jclass salga fuera de �mbito.

.�Pasar Argumentos al M�todo Java

El JNI proporciona varias formas de pasar argumentos al m�todo Java. La m�s frecuente, se pasan los argumentos siguiendo al ID del m�todo. Tambi�n hay dos variaciones de llamadas a m�todos que toman argumentos en un formato alternativo. Por ejemplo, la funci�n CallVoidMethodV recibe los argumentos en un va_list y la funci�n CallVoidMethodA espera los argumentos en un array de uniones jvalue. Los tipos del array de uniones jvalue son los siguientes.

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

Adem�s de la funci�n CallVoidMethod, el JNI tambi�n soporta funciones de llamada a m�todos con tipos de retorno, como CallBooleanMethod, CallIntMethod, etc. El tipo de retorno de la funci�n de llamada a m�todo debe corresponder con el tipo del m�todo Java que se desea invocar.

.�Llamar a M�todos Est�ticos

Se puede llamar a m�todos est�ticos Java de forma similar a como se llama a m�todos de ejemplar. Se deben seguir estos pasos.

  • Obtener el ID del m�todo utilizando la funci�n JNI GetStaticMethodID en vez la funci�n GetMethodID.
  • Pasar la clase, el ID del m�todo y los argumentos a la familia de funciones de llamadas a m�todos est�ticos: CallStaticVoidMethod, CallStaticBooleanMethod, etc.

Si se comparan las funciones de llamada para m�todos de ejemplar y est�ticos, veremos que las funciones de llamada a m�todos de ejemplar reciben el object, en vez de la clase, como el segundo argumento, seguido por el arguemnto JNIEnv. Por ejemplo, supongamos, que hemos a�adido un m�todo est�tico.

   static int incDepth(int depth) {return depth + 1};

dentro de Callback.java. Podremos llamar a este m�todo est�tico incDepth desde Java_Callback_nativeMethod utilizando las siguientes funciones JNI.

JNIEXPORT void JNICALL
Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)
{
  jclass cls = (*env)->GetObjectClass(env, obj);
  jmethodID mid = (*env)->GetStaticMethodID(env, cls, "incDepth", "(I)I");
  if (mid == 0)
    return;
  depth = (*env)->CallStaticIntMethod(env, cls, mid, depth);

.�Llamar a M�todos de Ejemplar de una Superclase

Se puede llamar a m�todos definidos en una superclase que se han sobreescrito en la clase a la que pertenece el objeto. El JNI proporciona un conjunto de funciones CallNonvirtual<type>Method para este prop�sito. Para llamar a un m�todo de ejemplar de una superlclase, se debe hacer lo siguiente.

  • Obtener el ID del m�todo de la superclase utilizando GetMethodID en vez de GetStaticMethodID.
  • Pasar el objeto, la superclase, el ID del m�todo, y los arguemntos a la familia de funciones de llamadas no vituales: CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod, etc.

Ser� raro que necesitemos invocar a m�todos de ejemplar de una superclase. Esta facilidad es similar a llamar al m�todo de una clase, digamos f, utilizando.

super.f();

en el lenguaje Java.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO