Acceder a Arrays Java

El JNI utiliza el tipo jarray para representar referencias a arrays java. Al igual que con jstring, no se puede acceder directamente a los tipos jarray desde el código nativo, se deben utilizar las funciones proporcionadas por el JNI que permiten obtener punteros a los elementos de arrays de enteros.

Nuestro segundo ejemplo, IntArray.java, contiene un método nativo que suma el total de un array de enteros pasado por la aplicación Java. No se puede implementar el método nativo direccionando directamente los elementos del array. El siguiente código intenta incorrectamente acceder directamente a los elementos del array:

/* This program is illegal! */
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
  int i, sum = 0;
  for (i=0; i<10; i++)
    sum += arr[i];
La forma correcta de implementar la función anterior, se puede ver en el método nativo IntArray.c. En este ejemplo, se utiliza una función JNI para obtener la longitud del array. Luego se pueden recuperar los elementos. Y finalmente se utiliza una tercera función del JNI para liberar la memoria del array.

Acceder a un Array de Elementos Primitivos

Primero se obtiene la longitud del array llamando a la función GetArrayLength del JNI. Observa que al contrario que en los array del C, los array Java almacenan información sobre la longitud.
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
  int i, sum = 0;
  jsize len = (*env)->GetArrayLength(env, arr);
Luego se obtiene un puntero a los elementos del array. Nuestro ejemplo contiene un array de enteros, por eso utilizamos la función del JNI GetIntArrayElements para obtener este puntero. (El JNI proporciona un juego de funciones para obtener punteros a elementos del array; se utiliza la función que corresponde con el tipo primitivo del array). Una vez obtenido el puntero, se pueden utilizar las operacones normales del C sobre el array de enteros resultante.
  jint *body = (*env)->GetIntArrayElements(env, arr, 0);
  for (i=0; i<len; i++)
    sum += body[i];
En general, el recolector de basura podría eliminar los arrays Java. Sin embargo, la máquina virtual garantiza que el resultado de GetIntArrayElements apunta a un array de enteros inamovible. El JNI o bien baja el "pin" del array "marcándolo" o hace una copia del array en una memoria inamovible. A causa de esto, el código nativo debe llamar a ReleaseIntArrayElements cuando ha terminado de utilizar el array de esta forma:
  (*env)->ReleaseIntArrayElements(env, arr, body, 0);
  return sum;
}
ReleaseIntArrayElements permite al JNI copiar de vuelta y liberar la memoria referenciada por el parámetro body si es una copia del array original Java, "desmarca" el array java que fue marcado en la memoria. No olvides llamar a ReleaseIntArrayElements. Si se olvida hacer esta llamada el array permanece marcado por un largo periodo de Tiempo. Y la máquina virtual no podrá reclamar la memoria utilizada para almacenar la copia inamovible del array.

El JNI proporciona un conjunto de funciones para acceder a los elementos de arrays de los distintos tipos primtivos:

Acceder a un Número Pequeño de Elementos

Observa que la función Get<type>ArrayElements potencialmente podría copiar el array entero. Podríamos querer copiar un número limitado de elementos, especialmente si el array es grande. Si sólo estamos interesados en un número pequeño de elementos (en un array potencialmente grande), deberíamos utilizar las funciones Get/Set<type>ArrayRegion. Estas funciones permiten acceder, mediante copia, a un conjunto pequeño de elementos de un array.

Acceder a Arrays de Objetos

El JNI proporciona un conjunto separado de funciones para acceder a elementos de arrays de objetos. Se pueden utilizar estas funciones para obtener y seleccionar objetos individuales del array. Observa que no se puede obtener el array de objetos completo de una sola vez.

Ozito