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

Unable to set Java int array field from JNI

本文关键字:Int 数组 字段 Java 设置 JNI      更新时间:2023-10-16

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

我该怎么做?

我已经尝试了几种方法来执行此操作,但是每次我获得带有无效地址的SIGSEGV error。对于其他字段,我正在使用

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

方法,但是没有类似的方法对int数组,有吗?因此,我尝试通过调用我的类方法并提供int数组作为参数来设置此字段,但是此功能失败并返回了SIGSEGV error

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

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

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

但是这里第一个方法返回始终为空。

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

有人可以告诉我我犯了一个错误还是以错误的方式使用它?
还有其他方法可以设置此数据吗?

这会产生一个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 array 。因此,这个...

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

...是不正确的。idArray不是正确的类型(并且不会腐烂到正确的指针(,用于您要尝试调用的Java方法的相应参数。

另一方面,这个...

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

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

如果您需要创建 new Java int[] ,则

  1. 使用JNI的NewIntArray()函数,该功能以您指定的长度返回jintArray。然后
  2. 使用适当的JNI方法来设置元素(见下文(,最后
  3. SetObjectField()将数组直接分配到目标对象的字段,或者使用对象的设置方法进行此操作,就像您的第一次尝试一样。

至于设置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);

对于第一种方法,您需要首先创建一个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);