Estoy desarrollando una aplicación para Android y estoy recibiendo datos de la cámara de un lib en C ++. Necesito enviar estos datos de C ++ para el código Java. Por esto, yo estoy usando JNI. Soy capaz de establecer diferentes campos en Java desde el los datos de C ++ (como el nombre o el tipo de la cámara) y JNI, pero soy incapaz de establecer el campo ID porque es una uint8_t
matriz.
¿Cómo puedo hacer esto?
Ya he intentado varias maneras de hacer esto, pero cada vez que tengo una SIGSEGV error
con una dirección no válida. Para otros campos que estoy usando
env->Set<Primitives>Field(jobject, jfieldID, value)
método, pero no existen métodos como que para int
array, hay? Por lo tanto, he tratado de establecer este campo llamando a un método de mi clase y proporcionar la int
matriz como parámetro pero esta función de error y devolvió el SIGSEGV error
.
Entonces, busqué en la web y he tratado de establecer el campo a través
env->GetObjectField(jobject, jfieldID)
y
env->SetIntArrayRegion(jintArray, start, end, myIntArray)
pero aquí el primer método devuelve siempre nula.
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
Alguien me puede decir si he cometido un error o estoy usando el camino equivocado?
¿Hay alguna otra manera de establecer estos datos?
Esto produce una C ++ array con elementos de tipo 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] }
Es importante entender que esto no es un Java matriz . Por lo tanto, este ...
///////// FIRST WAY ///////// jmethodID setIDCamMID = env->GetMethodID(env->GetObjectClass(camera), "setIDCam", "([I)V"); env->CallVoidMethod(camera, setIDCamMID, idArray);
... Es incorrecto. idArray
No es el tipo correcto (y no decae a un puntero al tipo correcto) para el parámetro correspondiente al método Java que está intentando invocar.
Por otro lado, este ...
///////// SECOND WAY ///////// jintArray jintArray1 = (jintArray)env->GetObjectField(camera, camIDField); env->SetIntArrayRegion(jintArray1, 0, 16, idArray);
... está bien, siempre y cuando el campo ya contiene una referencia a una int[]
de la longitud de al menos 16 . Dado que no funcionó en su caso, puedo considerar que el valor inicial del campo no satisface dicho criterio.
Si necesita crear un nuevo Javaint[]
, a continuación,
- utilizar JNI de
NewIntArray()
función, que devuelve unajintArray
con la longitud que se especifique. Luego - utilizar los métodos JNI adecuadas para establecer los elementos (ver más abajo), y finalmente
- o bien asignar la matriz directamente al campo del objeto de destino con
SetObjectField()
o utilizar un método seleccionador del objeto para hacerlo, ya que en su primer intento.
En cuanto a la configuración de los elementos de la matriz de Java, SetIntArrayRegion()
no tendrán ningún problema para que (dada una matriz real de Java de longitud suficiente), pero eso se requieren para asignar una matriz nativa separada de primitivas (su idArray
) de la que copiar los valores. Un enfoque ligeramente más eficiente sería utilizar GetPrimitiveArrayCritical()
para dejar de Java proporciona el buffer - posiblemente un puntero directo a los datos internos - y luego ReleasePrimitiveArrayCritical()
cuando haya terminado. Algo como esto:
// 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);