Android JNI array operation

There are two kinds of array operations in JNI, basic data type array and object array. JNI treats basic data type array and object array differently.

Array of basic data types

For the array of basic data types, JNI has the structure corresponding to Java, which is similar to the use of basic data types.

JNI array type corresponding to Java array type is mentioned in Android JNI basic knowledge. For example, Java int array corresponds to jintArray, and boolean array corresponds to jbooleanArray.

Like the String operation, JNI provides the corresponding conversion functions: GetArrayElements, ReleaseArrayElements.

 intArray = env->GetIntArrayElements(intArray_, NULL);
 env->ReleaseIntArrayElements(intArray_, intArray, 0);

In addition, JNI provides the following functions:

  • GetTypeArrayRegion / SetTypeArrayRegion

Copy the contents of the array to the C buffer, or the contents of the buffer to the array.

  • GetArrayLength

Get the number of elements in the array, that is, the length.

  • NewTypeArray

Returns an array of the specified data type and assigns a value to the array of the specified type through SetTypeArrayRegion.

  • GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical

Just like the operation in String, it returns a direct pointer to an array of specified basic data types. There is no blocking operation between the two operations.

The actual operation is as follows:

// Java passes arrays to Native for array summation
private native int intArraySum(int[] intArray, int size);

The corresponding C + + code is as follows:

 JNIEXPORT jint JNICALL
 Java_com_glumes_cppso_jnioperations_ArrayTypeOps_intArraySum(JNIEnv *env, jobject instance,
                                                              jintArray intArray_, jint num) {
     jint *intArray;
     int sum = 0;
     // Operation method 1:
     // Like getUTFString, native memory will be applied
     intArray = env->GetIntArrayElements(intArray_, NULL);
     if (intArray == NULL) {
        return 0;
    }
        // Get the length of the array
    int length = env->GetArrayLength(intArray_);
    LOGD("array length is %d", length);
    for (int i = 0; i < length; ++i) {
        sum += intArray[i];
    }
    LOGD("sum is %d", sum);

    // Operation method 2:
    jint buf[num];
    // Get array contents through GetIntArrayRegion method
    env->GetIntArrayRegion(intArray_, 0, num, buf);
    sum = 0;
    for (int i = 0; i < num; ++i) {
        sum += buf[i];
    }
    LOGD("sum is %d", sum);
    // Don't forget to free memory after use
    env->ReleaseIntArrayElements(intArray_, intArray, 0);
    return sum;
    }

If you need to return an array of basic data types from JNI, the corresponding code is as follows:

// Return array of basic data types from Native
private native int[] getIntArray(int num);

The corresponding C + + code is as follows:

/**
  * Return int array from Native, mainly call set < type > arrayregion to fill in data, other data types are similar to operation
  */
 extern "C"
 JNIEXPORT jintArray JNICALL
 Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getIntArray(JNIEnv *env, jobject instance,
                                                              jint num) {
     jintArray intArray;
     intArray = env->NewIntArray(num);

    jint buf[num];
    for (int i = 0; i < num; ++i) {
        buf[i] = i * 2;
    }

    // Use setIntArrayRegion to assign values
    env->SetIntArrayRegion(intArray, 0, num, buf);
    return intArray;
}

In the above example, the related operations are basically used. It can be found that most of the operations are similar to those of String.

object array

For an object array, that is, an array of reference types, each type in the array is a reference type. JNI only provides the following functions to operate.

  • GetObjectArrayElement / SetObjectArrayElement

Unlike the basic data type, you cannot get all the object elements in the data at once or copy multiple object elements to the buffer at one time. Only the above functions can access or modify the element content of the specified location.

Both strings and arrays are reference types, so they can only be accessed through the above methods.

For example, create a 2D integer array in JNI and return:

// Returns a two-dimensional integer array from Native, which is equivalent to a one-dimensional integer array. Each item in the array is an array
private native int[][] getTwoDimensionalArray(int size);

The particularity of two-dimensional array is that it can be regarded as one-dimensional array, in which every item of array is one-dimensional array.

The specific C + + code is as follows:

/**
  * Return a two-dimensional integer array from Native
  */
 extern "C"
 JNIEXPORT jobjectArray JNICALL
 Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getTwoDimensionalArray(JNIEnv *env,
                                                                         jobject instance,
                                                                         jint size) {
    // Declare an array of objects
    jobjectArray result;
    // Find the specific object type in the object array, [I refers to the array type
    jclass intArrayCls = env->FindClass("[I");

    if (intArrayCls == NULL) {
        return NULL;
    }
    // Equivalent to initializing an array of objects, using the specified object type
    result = env->NewObjectArray(size, intArrayCls, NULL);

    if (result == NULL) {
        return NULL;
    }
    for (int i = 0; i < size; ++i) {
        // Buffer used to fill an integer array with data
        jint tmp[256];
        // Declare an integer array
        jintArray iarr = env->NewIntArray(size);
        if (iarr == NULL) {
            return NULL;
        }
        for (int j = 0; j < size; ++j) {
            tmp[j] = i + j;
        }
        // Fill an integer array with data
        env->SetIntArrayRegion(iarr, 0, size, tmp);
        // Fill the position specified for the object array with data, which is a one-dimensional integer array
        env->SetObjectArrayElement(result, i, iarr);
        // Release local reference
        env->DeleteLocalRef(iarr);
    }
    return result;
}

You first need to use the NewObjectArray method to create an array of objects.

Then, when you use the SetObjectArrayElement function to fill in the data, you need to build the object corresponding to each location. In this case, NewIntArray is used to create an object and fill it with data before assigning it to the object array.

Through a for loop, the assignment to the object array is completed.

When creating an object array, one of the operations is to find the corresponding object type through the findClass method. The parameter [I of findClass involves the signature conversion between Java and JNI.

Java and JNI signature conversion

In the previous article, the conversion relationship of data type format corresponding to Java and JNI is listed in table. Now, the conversion relationship of signature corresponding to Java and JNI is listed.

The signature here refers to that when finding the corresponding data type and method in Java in JNI, the signature in Java needs to be converted into what JNI can recognize.

Signature conversion for classes

For the conversion of classes or interfaces in Java, you need to use the fully qualified names of classes or interfaces in Java. For example, the JNI corresponding to String type is described as:

java/lang/String     // Change to / 

For array type, we use [to represent the array and then convert it with the signature of a field.

[I         // Represents an array of one-dimensional integers, and I represents an integer
[[I        // Represents a two-dimensional integer array
[Ljava/lang/String;      // Represents an array of one-dimensional strings, 

Signature conversion for fields

Conversion of corresponding basic type fields:

For the field signature conversion of reference type, it starts with the uppercase letter L, then the signature conversion of class, and ends with.

Signature conversion for methods

For the conversion of method signature description, firstly, all parameters in the method are converted to corresponding field description, and all parameters are written in parentheses, then the return value type description of the method is followed immediately outside the parentheses.

It should be noted that there is no space in the description transformation corresponding to JNI.

After understanding and mastering these transformations, more operations can be carried out to realize the mutual call between Java and C + +.

For example, there is a custom Java class, and then a field value of the object array of the class is printed in Native.

private native void printAnimalsName(Animal[] animal);

The specific C + + code is as follows:

/**
  * Print information in an array of objects
  */
 extern "C"
 JNIEXPORT void JNICALL
 Java_com_glumes_cppso_jnioperations_ArrayTypeOps_printAnimalsName(JNIEnv *env, jobject instance,
                                                                   jobjectArray animals) {
     jobject animal;
     // Array length
    int size = env->GetArrayLength(animals);
    // Corresponding class in array
    jclass cls = env->FindClass("com/glumes/cppso/model/Animal");
    // Field description corresponding to class
    jfieldID fid = env->GetFieldID(cls, "name", "Ljava/lang/String;");
    // Class's field specific value
    jstring jstr;
    // Class field concrete value converted to C/C + + string
    const char *str;

    for (int i = 0; i < size; ++i) {
        // Get every element in the array
        animal = env->GetObjectArrayElement(animals, i);
        // The value of each element's specific field
        jstr = (jstring) (env->GetObjectField(animal, fid));
        str = env->GetStringUTFChars(jstr, NULL);
        if (str == NULL) {
            continue;
        }
        LOGD("str is %s", str);
        env->ReleaseStringUTFChars(jstr, str);
    }
}

Reference resources

1.https://github.com/glumes/AndroidDevWithCpp

82 original articles published, praised 23, visited 80000+
Private letter follow

Tags: Java Android github

Posted on Sun, 12 Jan 2020 01:48:38 -0500 by calevans