I'm developing an Android App and I'm receiving camera data from a lib in C++. I need to send this data from C++ to the Java code. For this, I'm using JNI. I'm able to set different fields in Java from the JNI and the C++ data (like the name or the type of the camera), but I'm unable to set the ID Field because it's an uint8_t
array.
How can I do this?
I already tried several ways to do this but each time I've got an SIGSEGV error
with an invalid address. For others fields I'm using
env->Set<Primitives>Field(jobject, jfieldID, value)
method but there are no methods like that for int
array, are there?
So, I've tried to set this field by calling a method from my class and provide the int
array as parameter but this function failed and returned the SIGSEGV error
.
Then, I searched on the web and I tried to set the field through
env->GetObjectField(jobject, jfieldID)
and
env->SetIntArrayRegion(jintArray, start, end, myIntArray)
but here the first method returns always null.
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
Can someone tell me if I made a mistake or am using it the wrong way?
Is there any other way to set this data?
This yields a C++ array with elements of type 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] }
It is important to understand that that is not a Java array. Therefore, this ...
///////// FIRST WAY ///////// jmethodID setIDCamMID = env->GetMethodID(env->GetObjectClass(camera), "setIDCam", "([I)V"); env->CallVoidMethod(camera, setIDCamMID, idArray);
... is incorrect. idArray
is not the right type (and does not decay to a pointer to the right type) for the corresponding parameter to the Java method you are trying to invoke.
On the other hand, this ...
///////// SECOND WAY ///////// jintArray jintArray1 = (jintArray)env->GetObjectField(camera, camIDField); env->SetIntArrayRegion(jintArray1, 0, 16, idArray);
... is ok, provided that the field already contains a reference to an int[]
of length at least 16. Since it didn't work for you, I take it that the field's initial value does not satisfy that criterion.
If you need to create a new Java int[]
, then
NewIntArray()
function, which returns a jintArray
with the length you specify. ThenSetObjectField()
or use the object's setter method to do so, as in your first attempt.As for setting elements of the Java array, SetIntArrayRegion()
will work fine for that (given an actual Java array of sufficient length), but that does require you to allocate a separate native array of primitives (your idArray
) from which to copy the values. A slightly more efficient approach would be to use GetPrimitiveArrayCritical()
to let Java provide the buffer -- possibly a direct pointer to the internal data -- and then ReleasePrimitiveArrayCritical()
when you're done. Something like this:
// 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With