无法从 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);
... 可以,前提是该字段已经包含对长度至少为 16 的 int[]
的引用。由于它对你不起作用,我认为该字段的初始值不满足该标准。
如果需要新建Javaint[]
,则
- 使用 JNI 的
NewIntArray()
函数,它 returns 一个 jintArray
具有您指定的长度。然后
- 使用适当的 JNI 方法设置元素(见下文),最后
- 使用
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);
我正在开发一个 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);
... 可以,前提是该字段已经包含对长度至少为 16 的 int[]
的引用。由于它对你不起作用,我认为该字段的初始值不满足该标准。
如果需要新建Javaint[]
,则
- 使用 JNI 的
NewIntArray()
函数,它 returns 一个jintArray
具有您指定的长度。然后 - 使用适当的 JNI 方法设置元素(见下文),最后
- 使用
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);