如何通过JNI设置Java对象的多维数组字段

时间:2014-03-07 18:28:56

标签: java c++ multidimensional-array java-native-interface field

在我的本机C ++代码中,有一个必须返回Java对象的函数。使用JNI从Java代码中调用此函数。在返回的对象内部,有一个二维双数组字段,必须在返回对象之前填充。虽然有关于在jobject中设置数组字段的示例代码,但是没有明确的方法来设置多维数组字段,无论它们是基元数组还是其他对象。一个人到底是怎么做到的?

1 个答案:

答案 0 :(得分:2)

经过一番研究,我发现诀窍是将多维数组字段视为一个jobjects数组。在这个例子中,这些jobject实际上是双精度数组。这可以根据需要继续进行多个维度。包含有问题的多维数组字段的对象是:

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

其中多维数组实际上是4乘4变换矩阵。为了证明拥有包含数组的对象的必要性,还有另一个String字段。有了这个,返回ObjectTransform的本机C ++函数如下:

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

可以像往常一样使用以下签名从Java内部调用本机函数:

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

从这里开始,设置任意对象的多维数组字段的过程非常简单:代替[[D,编写[[com.example.exampleclass;。而不是NewDoubleArray,请致电NewObjectArray。而不是SetDoubleArrayRegion,而是将对象数组的所有元素分别按SetObjectArrayElement设置为您事先创建的对象。

对此代码的所有改进当然都是受欢迎的。