简体   繁体   中英

How to set multidimensional array field of Java object through JNI

In my native C++ code, there is a function that must return a Java object. This function is called from within the Java code using JNI. Inside the returned object, there is a two dimensional double array field that must be filled before the object is returned. While there are example codes regarding setting an array field inside a jobject, there is no clear way of setting multidimensional array fields, whether they be arrays of primitives or other objects. How exactly does one achieve this?

After researching a bit, I found that the trick is to treat the multidimensional array field as an array of jobjects. In this example, these jobjects are in fact arrays of doubles. This can be continued for as many dimensions as needed. The object that contains the multidimensional array field in question is:

package com.example;

public class ObjectTransform{

    public String name;

    public double[][] transform;

    public ObjectTransform(){
        name = "";
        transform = new double[4][4];
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                transform[i][j] = i == j ? 1 : 0;
    }
}

where the multidimensional array is in fact a 4 by 4 transform matrix. Just to prove the necessity of having an object containing the array, there is another String field. Having this, the native C++ function that returns an ObjectTransform is the following:

extern "C" JNIEXPORT jobject JNICALL Java_com_example_examplefunction(
    JNIEnv* env, jclass* class_){

    //Get class identifier of our object
    jclass cls = env->FindClass("com/example/ObjectTransform");

    //Get constructor identifier for our object
    jmethodID constructor = env->GetMethodID(cls,"<init>","()V");

    //Get field identifiers of our object
    //Transform is a two dimensional double array, denoted as [[D
    jfieldID nameID = env->GetFieldID(cls,"name","Ljava/lang/String;");
    jfieldID transformID = env->GetFieldID(cls,"transform","[[D"); 

    //Allocate object that we will return
    jobject jresult = env->NewObject(cls, constructor);

    //Set object name
    env->SetObjectField(jresult, nameID, env->NewStringUTF("some name"));

    /*
     * Build object transform matrix
     */

    //Get rows of the matrix in JVM heap space, we will change them
    jobjectArray rows = reinterpret_cast<jobjectArray>(
        env->GetObjectField(jresult,transformID));

    //Allocate some temporary variables
    jdoubleArray jrow;
    jdouble row[4];

    //Traverse rows
    for(int j=0;j<4;j++){

        //Traverse columns
        for(int k=0;k<4;k++){

            //Set current element of the matrix accordingly
            row[k] = calculate_some_value(j,k);
        }

        //Temporarily allocate a new row in JVM heap space 
        //No need to unpin an array allocated with New...Array in the end
        jrow = env->NewDoubleArray(4);

        //Fill the newly allocated row
        env->SetDoubleArrayRegion(jrow,0,4,row);

        //Write the newly filled row to the appropriate row of our matrix
        env->SetObjectArrayElement(rows,j,jrow);
    }

    return jresult;
}

The native function can be called from inside Java as usual with the following signature:

package com.example;
...
private native ObjectTransform examplefunction();

From here, setting multidimensional array fields of arbitrary objects follows straightforwardly: Instead of [[D , write [[com.example.exampleclass; . Instead of NewDoubleArray , call NewObjectArray . Instead of SetDoubleArrayRegion , set all elements of the object array individually by SetObjectArrayElement to objects you have created beforehand.

All improvements on this code are of course welcome.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM