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.