无法从 JNI 设置 Java int 数组字段

Unable to set Java int array field from JNI

我正在开发一个 Android 应用程序,我正在从 C++ 库中接收相机数据。我需要将此数据从 C++ 发送到 Java 代码。为此,我正在使用 JNI。我可以根据 JNI 和 C++ 数据在 Java 中设置不同的字段(例如相机的名称或类型),但我无法设置 ID 字段,因为它是一个 uint8_t数组。

我该怎么做?

我已经尝试了几种方法来做到这一点,但每次我都收到一个 SIGSEGV error 的无效地址。对于其他字段,我使用

env->Set<Primitives>Field(jobject, jfieldID, value)

方法但是没有像 int 数组那样的方法,是吗? 因此,我尝试通过从我的 class 调用方法并提供 int 数组作为参数来设置此字段,但此函数失败并返回 SIGSEGV error.

然后,我在网上搜索,我尝试通过

设置字段
env->GetObjectField(jobject, jfieldID)

env->SetIntArrayRegion(jintArray, start, end, myIntArray)

但是这里第一个方法returns总是空的。

JavaVM * mJVM; //My Java Virtual Machine
jobject mCameraObject, mThreadObject; //Previously initialize to call functions in the right thread

void onReceiveCameraList(void *ptr, uint32_t /*id*/, my::lib::Camera *arrayCamera, uint32_t nbCameras) {

    JNIEnv *env;
    mJVM->AttachCurrentThread(&env, nullptr);
    if (env->ExceptionCheck())
        return;

    //Get Field, Method ID, Object and Class
    jclass cameraClass = env->GetObjectClass(mCameraObject);

    jfieldID camIDField = env->GetFieldID(cameraClass, "idCam", "[I");
    jfieldID camNameField = env->GetFieldID(cameraClass, "label", "Ljava/lang/String;");
    jfieldID camConnectedField = env->GetFieldID(cameraClass, "connected", "Z");
    jfieldID camTypeField = env->GetFieldID(cameraClass, "typeProduit", "B");

    jmethodID camReceptionMID = env->GetMethodID(env->GetObjectClass(mThreadObject), "onCamerasReception", "([Lcom/my/path/models/Camera;)V"); //Java function
    jobjectArray cameraArray = env->NewObjectArray(nbCameras, cameraClass, mCameraObject); //Object return in the functions

    //Put the cameras into the vector
    std::vector<my::lib::Camera> vectorCameras;
    if(!vectorCameras.empty())
        vectorCameras.clear();

    if ((arrayCamera != nullptr) && (nbCameras > 0)) {
        for (uint32_t i = 0; i < nbCameras; ++i) {
            vectorCameras.push_back(arrayCamera[i]);
        }
    }

    //Set the my::lib::Camera field into Java::Camera
    int c= 0;
    for (auto & cam : vectorCameras)
    {
        jobject camera = env->AllocObject(cameraClass); //Object Camera to add in cameraArray object

    // MY DATA TO SET ID FIELD ///
    jint idArray[16];
        for (int i = 0; i < 16 ; ++i) {
            idArray[i] = cam.idCamera.data[i]; // uint8_t cam.idCamera.data[16]
        }

    ///////// FIRST WAY  /////////
    jmethodID setIDCamMID = env->GetMethodID(env->GetObjectClass(camera), "setIDCam", "([I)V");
    env->CallVoidMethod(camera, setIDCamMID, idArray);

    ///////// SECOND WAY /////////
        jintArray jintArray1 = (jintArray)env->GetObjectField(camera, camIDField);
        env->SetIntArrayRegion(jintArray1, 0, 16, idArray);

    //Set<Primitives>Field : WORKING
        env->SetObjectField(camera, camNameField, env->NewStringUTF((const char *) cam.labelCamera));
        env->SetBooleanField(camera, camConnectedField, cam.isCameraConnected);
        jbyte type;
        if (cam.typeCamera == my::lib::TYPE_1 || cam.typeCamera == my::lib::TYPE_2 || cam.typeCamera == my::lib::TYPE_3) //type not known in JAVA
            type = 0;
        else
            type = cam.typeCamera;
        env->SetByteField(camera, camTypeField, type);

    //Put camera object into cameraArray object
        env->SetObjectArrayElement(cameraArray, c++, camera);
    }//for

    //Call Java method with cameraArray
    env->CallVoidMethod(mThreadObject, camReceptionMID, dpCameraArray);

}//onreceiveCamera

如果我弄错了或者使用方法不对,有人可以告诉我吗?
还有其他方法可以设置此数据吗?

对于第一种方法,您需要先创建一个Java int[],从idArray填充它,然后然后调用您的方法:

jintArray j_arr = env->NewIntArray(16);
env->SetIntArrayRegion(j_arr, 0, 16, idArray);
env->CallVoidMethod(camera, setIDCamMID, j_arr);

您的第二种方法不起作用,因为您从未调用过填充 idCam 字段的构造函数。 但是,您可以从 JNI 执行此操作:

env->SetObjectField(camera, camIDField, j_arr);

这将生成一个 C++ 数组,其元素类型为 jint:

    // MY DATA TO SET ID FIELD ///
    jint idArray[16];
        for (int i = 0; i < 16 ; ++i) {
            idArray[i] = cam.idCamera.data[i]; // uint8_t cam.idCamera.data[16]
        }

了解 不是 Java 数组 很重要。因此,这个...

    ///////// FIRST WAY  /////////
    jmethodID setIDCamMID = env->GetMethodID(env->GetObjectClass(camera), "setIDCam", "([I)V");
    env->CallVoidMethod(camera, setIDCamMID, idArray);

... 不正确。对于您尝试调用的 Java 方法的相应参数,idArray 不是正确的类型(并且不会衰减到指向正确类型的指针)。

另一方面,这...

    ///////// SECOND WAY /////////
        jintArray jintArray1 = (jintArray)env->GetObjectField(camera, camIDField);
        env->SetIntArrayRegion(jintArray1, 0, 16, idArray);

... 可以,前提是该字段已经包含对长度至少为 16int[] 的引用。由于它对你不起作用,我认为该字段的初始值不满足该标准。

如果需要新建Javaint[],则

  1. 使用 JNI 的 NewIntArray() 函数,它 returns 一个 jintArray 具有您指定的长度。然后
  2. 使用适当的 JNI 方法设置元素(见下文),最后
  3. 使用 SetObjectField() 将数组直接分配给目标对象的字段,或者使用对象的 setter 方法来这样做,就像您第一次尝试的那样。

至于设置 Java 数组的元素,SetIntArrayRegion() 可以正常工作(给定一个足够长的实际 Java 数组),但这确实需要您分配一个单独的原语本机数组(您的 idArray),从中复制值。一种稍微更有效的方法是使用 GetPrimitiveArrayCritical() 让 Java 提供缓冲区——可能是指向内部数据的直接指针——然后在完成后使用 ReleasePrimitiveArrayCritical()。像这样:

// It is assumed here that the length of the array is sufficient, perhaps because
// we just created this (Java) array.
jint *idArray = (jint *) env->GetPrimitiveArrayCritical(jintArray1, NULL);

for (uint32_t i = 0; i < nbCameras; ++i) {
    idArray[i] = cam.idCamera.data[i];
}
env->ReleasePrimitiveArrayCritical(jintArray1, idArray, 0);