JNI/NDK Development Guide (5) -- access array (basic type array and object array), jnindk

Source: Internet
Author: User

JNI/NDK Development Guide (5) -- access array (basic type array and object array), jnindk


Reprinted please indicate the source: http://blog.csdn.net/xyang81/article/details/42346165


Arrays in JNI can be divided into basic type arrays and object arrays. Their processing methods are different. All elements in the basic type array are the basic data types of JNI and can be accessed directly. All elements in the object array are referenced by a class instance or other arrays. Like string operations, you cannot directly access the array passed to the JNI layer by Java, you must select an appropriate JNI function to access and set the array objects at the Java layer. Read this article to learn about the ing between JNI and Java data types. If you do not know about the ing between JNI and Java data types, see JNI/NDK Development Guide (3) -- read JNI data types and ing relationships with Java data types. The following uses the int type as an example to describe how to access an array of basic data types. The object array type uses an example to create a two-dimensional array to demonstrate how to access it:


1. access basic types of Arrays

Package com. study. jnilearn; // access the basic type array public class IntArray {// calculate the sum of all elements in the array and the private native int sumArray (int [] arr) in the local code ); public static void main (String [] args) {IntArray p = new IntArray (); int [] arr = new int [10]; for (int I = 0; I <arr. length; I ++) {arr [I] = I;} int sum = p. sumArray (arr); System. out. println ("sum =" + sum);} static {System. loadLibrary ("IntArray ");}}
Local Code:

/* Do not edit this file-it is machine generated */# include <jni. h>/* Header for class com_study_jnilearn_IntArray */# ifndef _ EXIST # define _ EXIST # ifdef _ cplusplusextern "C" {# endif/** Class: com_study_jnilearn_IntArray * Method: sumArray * Signature: ([I) I */JNIEXPORT jint JNICALL struct (JNIEnv *, jobject, jintArray); # ifdef _ cplusplus} # 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 struct (JNIEnv * env, jobject obj, jintArray j_array) {jint I, sum = 0; jint * c_array; jint arr_len; // 1. get the array length arr_len = (* env)-> GetArrayLength (env, j_array); // 2. apply to store the buffer c_array = (jint *) malloc (sizeof (jint) * arr_len) of the java array element based on the array length and the Data Type of the array element; // 3. initialize the buffer memset (c_array, 0, sizeof (jint) * arr_len); printf ("arr_len = % d", arr_len); // 4. copy all elements in the Java array to 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} free (c_array); // 6. release the buffer for storing array elements return sum ;}
In the above example, a native method of sumArray is defined in Java. The parameter type is int [], which corresponds to the jintArray type in JNI. In the local code GetArrayLengthFunction to obtain the length of the array. It is known that the array is of the jintArray type. We can conclude that the element type of the array is jint, and then apply for a buffer of the corresponding size based on the length of the array and the array element type. If the buffer zone is not large, you can also apply for memory directly on the stack, which is more efficient, but less flexible. because the size of the Java array has changed, the local code is also modified. Call GetIntArrayRegionThe function copies all elements in the Java array to the C buffer, accumulates the sum of all elements in the array, and finally releases the C buffer that stores the elements in the java array, and returns the calculation result. The GetIntArrayRegion function has 1st parameters as the JNIEnv function pointer, 2nd parameters as Java array objects, 3rd parameters as the start index of the copy array, and 4th parameters as the copy array length, the first parameter is the copy destination. Is the calculation result:

In the previous example, we called the GetIntArrayRegion function to copy all the elements in the int array to the temporary C buffer, and then accessed the elements in the buffer in the local code to calculate the sum, JNI also provides the SetIntArrayRegion function corresponding to GetIntArrayRegion. The local code can use this function to modify the elements of all basic data type arrays. In addition, JNI also provides a series of Get/Release <Type> ArrayElements functions to directly obtain array element pointers, such as GetIntArrayElements, ReleaseArrayElements, GetFloatArrayElements, and ReleaseFloatArrayElements. Here we use this method to re-calculate the sum of array elements:

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; // The elements in the array may be discontinuous in the memory. JVM may copy all the original data to the buffer and then return the pointer c_array = (* env) to the buffer) -> GetIntArrayElements (env, j_array, NULL); if (c_array = NULL) {return 0; // JVM failed to copy the original data to the 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); // release the buffer that may be copied return sum ;}
The third parameter of GetIntArrayElements indicates whether the returned array pointer is the original array or the pointer that copies the original data to the temporary buffer. If it is JNI_TRUE: indicates the temporary buffer array pointer, JNI_FALSE: indicates the temporary original array pointer. During development, we do not care where it returns the array pointer. this parameter is set to NULL, but the obtained pointer must be verified, because when the raw data is not stored continuously in the memory, JVM will copy all the raw data to a temporary buffer and return the pointer to this temporary buffer. It is possible that the application fails due to insufficient memory when a temporary buffer memory space is opened, and NULL is returned.
Programmers who have written about Java know that all objects created in Java are automatically recycled by GC (Garbage Collector) and do not need to manage the memory like C/C ++. GC scans all created objects in real time for any references. If no references exist, GC immediately clears them. When we create an int array object, when we want to access the local code, we find that this object is being occupied by the GC thread, and the local code will remain in the blocking status, the access will not continue until the GC releases the Lock of this object. To avoid this, JNI provides the Get/ReleasePrimitiveArrayCritical function. Local Code will suspend the GC thread when accessing the array object. However, this restriction also applies to functions. During the Get/ReleasePrimitiveArrayCritical functions, you cannot call any local function or JNI function that blocks threads or waits for other threads in the JVM, the limit is the same as that of the Get/ReleaseStringCritical function that processes strings. Like the GetIntArrayElements function, the returned pointer to the array element. The following describes how to re-implement the functions in the previous example:

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;}

Summary:

1. For small and fixed-size arrays, selectGet/SetArrayRegionFunction to operate on array elements is the most efficient. This requires that the function allocate a temporary C buffer in advance to store array elements. You can apply for it dynamically on the Stack or using malloc on the Stack, of course, stack applications are the fastest. Some children's shoes may think that the access to the array element also needs to copy all the original data to the temporary buffer to access it, and the efficiency is low? What I want to tell you is that the cost of copying a small number of array elements is very small and can be ignored. Another advantage of this function is that it allows you to input a start index and length to access and operate on sub-array elements (the SetArrayRegion function can modify the array ), however, the input index and length should not be out of bounds. The function will check if the input index and length are out of bounds. If the input index and length are out of bounds, an ArrayIndexOutOfBoundsException exception will be thrown.

2. Use the Get/ReleasePrimitiveArrayCritical function pair if you do not want to pre-allocate the C buffer and the length of the original array is not determined, and the local code does not want to be blocked when getting the array element pointer, just like the Get/ReleaseStringCritical function pair, be very careful when using this pair of functions to avoid deadlocks.

3. Get/Release <type> ArrayElements functions are always safe. The JVM will selectively return a pointer, which may point to raw data or copy the original data.


Ii. Access Object Array

JNI provides two functions to access the object array. GetObjectArrayElement returns the elements at the specified position in the array, and SetObjectArrayElement modifies the elements at the specified position in the array. Different from the basic type, we cannot get all the object elements in the data at a time or copy Multiple object elements at a time to the buffer zone. Because the string and array are both reference types, you can only access the array elements in the string array or array through JNI functions such as Get/SetObjectArrayElement. The following example creates a two-dimensional int array by calling a local method, and then prints the content of the two-dimensional array:

package com.study.jnilearn;public class ObjectArray {private native int[][] initInt2DArray(int size);public static void main(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:
/* Do not edit this file-it is machine generated */# include <jni. h>/* Header for class com_study_jnilearn_ObjectArray */# ifndef _ classes # define _ classes # ifdef _ cplusplusextern "C" {# endif/** Class: com_study_jnilearn_ObjectArray Method: initInt2DArray * Signature: (I) [[I */JNIEXPORT jobjectArray JNICALL inline (JNIEnv *, jobject, jint); # ifdef _ cplusplus} # endif // ObjectArray. c # include "inline"/** Class: inline * Method: initInt2DArray * Signature: (I) [[I */JNIEXPORT jobjectArray JNICALL inline (JNIEnv * env, jobject obj, jint size) {jobjectArray result; jclass clsIntArray; jint I, j; // 1. obtain the reference clsIntArray = (* env)-> FindClass (env, "[I"); if (clsIntArray = NULL) {return NULL ;} // 2. create an array object (each element in it is represented by clsIntArray) result = (* env)-> NewObjectArray (env, size, clsIntArray, NULL); if (result = NULL) {return NULL;} // 3. assign a value to 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 ;}
Result:



The local function initInt2DArray first calls the JNI function FindClass to obtain a reference to an int-type two-dimensional array class, and passes the parameter "[I" to the FindClass descript (JNI type descriptor, ), which corresponds to the int [] type in JVM. If the int [] class fails to be loaded, FindClass returns NULL and then throws a java. lang. NoClassDefFoundError: [I exception.

Next, NewObjectArray creates a new array. The element types in this array are represented by intArrCls (int. The NewObjectArray function can only allocate the first dimension. The JVM does not have a data structure corresponding to the multi-dimensional array, and JNI does not provide similar functions to create a two-dimensional array. Because the two-dimensional arrays in JNI directly operate on the data structure in JVM, it is much more complicated than creating two-dimensional arrays in JAVA and C/C ++. Setting data for a two-dimensional array is also very direct. First, use NewIntArray to create a JNI int array and allocate space for each array element, then, use SetIntArrayRegion to copy the content in the buff [] buffer to the newly allocated one-dimensional array, and assign the int [] array to the jobjectArray array in sequence in the outer loop, A one-dimensional array forms a so-called two-dimensional array.

In addition, to avoid creating a large number of JNI local references in the loop, the JNI reference table overflows, therefore, DeleteLocalRef is called every time in the outer loop to remove the newly created jintArray reference from the reference table. In JNI, only the jobject and child classes belong to the referenced variables, which occupy the space of the referenced table. jint, jfloat, and jboolean are all basic type variables and do not occupy the referenced tablespace, that is, they do not need to be released. The maximum space of the referenced table is 512. If the referenced table exceeds this range, the JVM will be suspended.

Related Article

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.