BernardoGO BernardoGO - 1 month ago 7
C++ Question

JNI SetFloatArrayElement() not working

I'm trying to modify some values in a jfloatArray before returning it to the java Android code.
I've found that I can't use the regular way

newArray[i] = result[i];
to do it and instead I should do
env->SetFloatArrayElement(newArray,i,result[i]);
.

The problem is that this function throws the error:


[armeabi-v7a] Compile++ arm : tensorflow_mnist <= tensorflow_jni.cc
jni/./tensorflow_jni.cc: In function '_jfloatArray*
Java_jp_narr_tensorflowmnist_DigitDetector_detectDigit(JNIEnv*,
jobject, jintArray)': jni/./tensorflow_jni.cc:171:14: error: 'JNIEnv'
has no member named 'SetFloatArrayElement'
env->SetFloatArrayElement(newArray,i,result[i]);
^


Code:

JNIEXPORT jfloatArray JNICALL
TENSORFLOW_METHOD(detectDigit)(JNIEnv* env, jobject thiz, jintArray raw_pixels) {

jboolean iCopied = JNI_FALSE;
jint* pixels = env->GetIntArrayElements(raw_pixels, &iCopied);
jfloatArray newArray = env->NewFloatArray(2);
jfloat *result = process( reinterpret_cast<int*>(pixels) );


for(int i=0; i<2; ++i) {

//VLOG(0) << " (" << i << "): " << newArray[i];
//newArray[i] = result[i];
//env->SetFloatArrayElement(newArray,i,result[i]);
}


env->ReleaseIntArrayElements(raw_pixels, pixels, JNI_ABORT);
env->ReleaseFloatArrayElements(newArray, result, JNI_ABORT);

free(result);

return newArray;
}

Answer

I'm trying to modify some values in a jfloatArray before returning it to the java Android code. I've found that I can't use the regular way newArray[i] = result[i]; to do it and instead I should do env->SetFloatArrayElement(newArray,i,result[i]);.

What makes you think that? As the compiler informs you, JNI has no SetFloatArrayElement() function. There is a single-element-setting function only for Object arrays, i.e. SetObjectArrayElement().

There are several alternatives for dealing with primitive arrays.

  • The classic mechanism is to use the appropriate Get*ArrayElements() function to get an ordinary array, modify the array, and then ReleaseArrayElements(). Note also that with this approach, if you want to commit changes (as you do) then you must use mode 0 or JNI_COMMIT, not JNI_ABORT.

  • For quick-running uses such as yours that does not call other JNI functions, you could consider GetPrimitiveArrayCritical() and ReleasePrimitiveArrayCritical(). You should not do that if you do any I/O between the get and release, however.

  • For your specific case, however, I would suggest SetFloatArrayRegion(). There is a corresponding GetFloatArrayRegion(), but you don't need it because you don't care about pinning or about the initial values of the (Java) array elements.

Using the third alternative might look like this:

JNIEXPORT jfloatArray JNICALL
TENSORFLOW_METHOD(detectDigit)(JNIEnv* env, jobject thiz, jintArray raw_pixels) {

    jfloatArray newArray = env->NewFloatArray(2);
    jint* pixels = env->GetIntArrayElements(raw_pixels, NULL);

    jfloat *result = process( reinterpret_cast<int*>(pixels) );

    env->ReleaseIntArrayElements(raw_pixels, pixels, JNI_ABORT);
    env->SetFloatArrayRegion(newArray, 0, 2, result);

    free(result);

    return newArray;
}

If you could rely on the process() function running very quickly and without any possibility of blocking, then you could consider using GetPrimitiveArrayCritical() and ReleasePrimitiveArrayCritical() to access the pixel array. It is possible that that would be more efficient on account of avoiding making a copy of the pixel array, but it is by no means certain that the approach you are using already would make a copy. (Note in particular that the second argument to GetIntArrayElements() is an output variable; it reports on whether a copy has been made, but does not direct that).