JNI/NDK Development Guide (v)--access to arrays (primitive type arrays and object arrays)

Source: Internet
Author: User

Reprint Please specify Source:http://blog.csdn.net/xyang81/article/details/42346165

The arrays in JNI are divided into primitive type arrays and object arrays, which are handled differently, and all elements in the primitive type array are the basic data types of JNI and can be accessed directly. While all elements in an array of objects are references to instances or other arrays of a class, as with string manipulation, you cannot directly access the array that Java passes to the JNI layer, and you must select the appropriate JNI function to access and set the Java layer's array object. Read this article to assume that you have already understood the mappings between JNI and Java data types, and if you do not know the children's shoes, please visit the JNI/NDK Development Guide (iii)--JNI data types and mapping with Java data types read. The following is an example of an int type that shows how an array of basic data types is accessed, and an object array type demonstrates how to access it with an example of creating a two-dimensional array:

I. Accessing an array of primitive types

Package com.study.jnilearn;//accesses the base type array public class Intarray {//In the local code to find all elements in the array and private native int Sumarray (int[] arr); public static void Main (string[] args) {Intarray p = new Intarray (); int[] arr = new Int[10];for (int i = 0; i < Arr.len Gth i++) {Arr[i] = i;} int sum = P.sumarray (arr); System.out.println ("sum =" + sum);} static {system.loadlibrary ("Intarray");}}
Local Code:

/* Don't EDIT this file-it are machine generated */#include <jni.h>/* Header for class Com_study_jnilearn_intarray */#ifndef _included_com_study_jnilearn_intarray#define _included_com_study_jnilearn_intarray#ifdef __ Cplusplusextern "C" {#endif */* Class:com_study_jnilearn_intarray * Method:sumarray * Signature: ([i) I */jniexpor T jint jnicall Java_com_study_jnilearn_intarray_sumarray (jnienv *, Jobject, jintarray); #ifdef __cplusplus} #endif # endif//intarray.c#include "Com_study_jnilearn_intarray.h" #include <string.h> #include <stdlib.h>/* * Class:com_study_jnilearn_intarray * Method:sumarray * Signature: ([i) I */jniexport jint jnicall java_com_study_jn    Ilearn_intarray_sumarray (jnienv *env, Jobject obj, Jintarray j_array) {jint i, sum = 0;    Jint *c_array;    Jint Arr_len; 1.    Gets the array length Arr_len = (*env)->getarraylength (Env,j_array); 2.    The buffer C_array = (jint*) malloc (sizeof (jint) * Arr_len) of the Java array element is requested based on the array length and the data type of the set element. 3.   Initializing buffers memset (c_array,0,sizeof (jint) *arr_len);    printf ("Arr_len =%d", Arr_len); 4.    Copies all the elements in the Java array into the buffer (*env)->getintarrayregion (Env,j_array,0,arr_len,c_array);  for (i = 0; i < Arr_len; i++) {sum + = C_array[i]; 5.  Accumulate the sum of the array elements and} free (C_array); 6. Releases the buffer that stores the array elements return sum;
In the above example, a Sumarray native method is defined in Java, the parameter type is int[], corresponding to the Jintarray type in the JNI. In the local code, the length of the array is obtained first through the getarraylength function of the JNI, the known quantity group is the Jintarray type, the element type of the array is Jint, and the size of the buffer is requested based on the length of the array and the type of the element. If the buffer is small, of course, you can also directly on the stack to apply for memory, that is more efficient, but not so flexible, because the size of the Java array has changed, the local code is also modified. The getintarrayregion function is then called to copy all the elements in the Java array into the C buffer, accumulate the sum of all the elements in the array, and finally release the C buffer that stores the elements of the Java array and return the results of the calculation. The 1th parameter of the Getintarrayregion function is the JNIENV function pointer, the 2nd parameter is the Java array object, the 3rd parameter is the starting index of the copy array, the 4th parameter is the length of the copy array, and the 5th parameter is the copy destination. Is the result of the calculation:

in the previous example, we have called the Getintarrayregion function, copied all the elements in the int array into the C temporary buffer, and then accessed the elements in the buffer in the local code to achieve the sum calculation. JNI also provides a function setintarrayregion corresponding to the getintarrayregion, which can be used by local code to modify the elements of all arrays of basic data types. In addition, JNI provides a series of function get/release<type>arrayelements that directly obtain pointers to array elements, such as: Getintarrayelements, Releasearrayelements, Getfloatarrayelements, releasefloatarrayelements and so on. In this way, we re-implement the calculation of the elements of the array:

JNIEXPORT jint    Jnicall Java_com_study_jnilearn_intarray_sumarray2 (jnienv *env, Jobject obj, Jintarray j_array) {jint i, sum = 0;    Jint *c_array;    Jint Arr_len;    It is possible that the elements in the array are not contiguous in memory, and the JVM may replicate all the raw data to the buffer and then return the pointer to the buffer C_array = (*env)->getintarrayelements (env,j_array,null);   if (C_array = = NULL) {return 0;    JVM failed to replicate raw data to buffer} Arr_len = (*env)->getarraylength (Env,j_array);    printf ("Arr_len =%d\n", Arr_len);    for (i = 0; i < Arr_len; i++) {sum + = C_array[i]; } (*env)->releaseintarrayelements (Env,j_array, C_array, 0); Releases the buffer that may be copied return sum;} 
Getintarrayelements The third parameter indicates whether the returned array pointer is the original array, or a pointer to the temporary buffer of the original data, if Jni_true: Represents a temporary buffer array pointer, Jni_false: Represents a temporary original array pointer. In development, we don't care where it returns the array pointer, this parameter is NULL, but the pointer must be validated, because when the raw data is not contiguous in memory, the JVM copies all the raw data into a temporary buffer and returns a pointer to the temporary buffer. It is possible that when a temporary buffer memory space is requested, there will be insufficient memory to cause the request to fail, and Null will be returned.
Programmers who have written Java know that objects created in Java are automatically reclaimed by the GC (garbage collector) and do not require programmers to manage their own memory in the same way that C + + + does. The GC scans all created objects in real time for references and clears them immediately if no references are available. When we create an array object like int, when we find that the object is being consumed by the GC thread when the local code wants to access it, the local code will remain blocked until the GC releases the lock on the object before it can continue to access. To avoid this, JNI provides the Get/releaseprimitivearraycritical function, where local code pauses the GC thread when accessing an array object. However, there is a limit to the use of this function, and during get/releaseprimitivearraycritical these two functions cannot call any local or JNI functions that would cause the thread to block or wait for other threads in the JVM, and the get/to handle the string. The releasestringcritical function limits the same. This is the same function as the Getintarrayelements function, which returns a pointer to an array element. The functionality in the previous example is re-implemented in this way:

Jniexport jint jnicall Java_com_study_jnilearn_intarray_sumarray (jnienv *env, Jobject obj, Jintarray j_array) {    Jint i, sum = 0;    Jint *c_array;    Jint Arr_len;    Jboolean iscopy;    C_array = (*env)->getprimitivearraycritical (env,j_array,&iscopy);    printf ("Iscopy:%d \ n", iscopy);    if (C_array = = NULL) {        return 0;    }    Arr_len = (*env)->getarraylength (Env,j_array);    printf ("Arr_len =%d\n", Arr_len);    for (i = 0; i < Arr_len; i++) {        sum + = C_array[i];    }    (*env)->releaseprimitivearraycritical (env, J_array, C_array, 0);    return sum;}


1, for a small amount of fixed-size arrays, you should choose the get/setarrayregion function to manipulate the array elements is the most efficient. Because this pair of functions requires allocating a temporary c buffer in advance to store the array elements, you can apply it directly on the stack or on the heap with malloc, which is, of course, the fastest application on the stack. Children's shoes might think that accessing an array element also requires that the original data be all copied one copy to the temporary buffer to be accessible and feel inefficient? What I want to tell you is that the cost of copying a small array of elements like this is very small and can almost be ignored. Another advantage of this function is that it allows you to pass in a start index and length to implement access and manipulation of the elements of a child array (the Setarrayregion function can modify the array), but the passed index and length do not cross over, and the function checks A ArrayIndexOutOfBoundsException exception is thrown if the bounds are exceeded.

2, if you do not want to pre-allocate the C buffer, and the original array length is not deterministic, and the local code does not want to get the array element pointer is blocked, use the get/releaseprimitivearraycritical function pair, like get/ releasestringcritical function pairs, use this pair of functions with great care to avoid deadlocks.

3. The Get/release<type>arrayelements series function is always safe, and the JVM will selectively return a pointer that may point to the original data or to the copy of the original data.

Ii. accessing an array of objects

JNI provides two functions to access an array of objects, Getobjectarrayelement returns the element at the specified position in the array, setobjectarrayelement modifies the element at the specified position in the array. Unlike the base type, we cannot get all the object elements in the data at once or copy multiple object elements to the buffer at once. Because strings and arrays are reference types, only JNI functions such as get/setobjectarrayelement can access array elements in a string array or array. The following example creates a two-dimensional int array by calling a local method, and then prints the contents of the two-dimensional array:

Package Com.study.jnilearn;public class Objectarray {private native int[][] Initint2darray (int size);p ublic static void M  Ain (string[] args) {Objectarray obj = new Objectarray (); int[][] arr = Obj.initint2darray (3); for (int i = 0; i < 3; i++) {for (int j = 0; J < 3; J + +) {System.out.format ("arr[%d][%d] =%d\n", I, J, Arr[i][j]);}} static {system.loadlibrary ("Objectarray");}}
Local Code:
/* Don't EDIT this file-it are machine generated */#include <jni.h>/* Header for class Com_study_jnilearn_objectar Ray */#ifndef _included_com_study_jnilearn_objectarray#define _included_com_study_jnilearn_objectarray#ifdef __ Cplusplusextern ' C ' {#endif/* * Class:com_study_jnilearn_objectarray * Method:initint2darray * Signature: (i) [[I */jniexport jobjectarray jnicall Java_com_study_jnilearn_objectarray_initint2darray (JNIENV *, Jobject, jint); #ifdef _ _cplusplus} #endif #endif//objectarray.c#include "com_study_jnilearn_objectarray.h"/* class:com_study_jnilearn_ Objectarray * Method:initint2darray * Signature: (i) [[I */jniexport Jobjectarray jnicall java_com_study_jnilearn_objec    Tarray_initint2darray (jnienv *env, Jobject obj, jint size) {Jobjectarray result;    Jclass Clsintarray;    Jint i,j;    1. Obtain a reference to an int type two-dimensional array class Clsintarray = (*env)->findclass (env, "[I");    if (Clsintarray = = null) {return null; }//2. Create an Array object (each element in the Clsintarray indicated) result = (*env)->newobjectarray (env,size,clsintarray,null);    if (result = = null) {return null;        }//3. Assign a value for the array element for (i = 0; i < size; ++i) {jint buff[256];        Jintarray intarr = (*env)->newintarray (env,size);        if (intarr = = null) {return null;        } for (j = 0; J < size; J + +) {Buff[j] = i + j;        } (*env)->setintarrayregion (Env,intarr, 0,size,buff);        (*env)->setobjectarrayelement (Env,result, I, intarr);    (*env)->deletelocalref (Env,intarr); } return result;

The local function Initint2darray first invokes the JNI function Findclass obtains a reference to a two-dimensional array class of type int, and the argument passed to Findclass "[I" is the Jni class descript (JNI type descriptor, which is described in detail later), It corresponds to the int[] type in the JVM. If the int[] class fails to load, Findclass returns null and throws a java.lang.NoClassDefFoundError: [I exception.

Next, Newobjectarray creates a new array in which the element type is represented by the INTARRCLS (int[]) type. The function Newobjectarray can only allocate the first dimension, the JVM does not have a data structure corresponding to the multidimensional array, and JNI does not provide a similar function to create a two-dimensional array. Because a two-dimensional array in JNI directly operates on data structures in the JVM, it is much more complex to create two-dimensional arrays than Java and C + +. The way to set the data for a two-dimensional array is also very straightforward, first create a JNI int array with Newintarray , allocate space for each array element, and then use Setintarrayregion to buff[] The contents of the buffer are copied into the newly allocated one-dimensional array, and finally the int[] array is assigned to the Jobjectarray array in the outer loop, and a one-dimensional array in the one-dimension array forms a so-called two-dimensional array.

In addition, in order to avoid creating a large number of JNI local references within the loop, causing the JNI reference table to overflow, call deletelocalref each time in the outer loop to remove the newly created Jintarray reference from the reference table. In Jni, only Jobject and subclasses are reference variables, occupy the reference table space, Jint,jfloat,jboolean, etc. are basic type variables, do not occupy the reference table space, that is, do not need to be freed. The reference table has a maximum space of 512, and if this range is exceeded, the JVM will be hung out.

JNI/NDK Development Guide (v)--access to arrays (primitive type arrays and object arrays)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.