Search code examples
androidc++callbackandroid-ndkjava-native-interface

How can I send a int as a parameter with getApplicationJniMethodId?


This is how I do it for String:

 void SampleSwitchCamera(const char *name)
{
    jboolean isAttached;
    JNIEnv *env;
    jmethodID mid;
    jstring js;
    LOGE("SampleSwitchCamera Begin");
    env = getJniEnv(&isAttached);
    if (env == NULL)
            goto FAIL0;

    mid = getApplicationJniMethodId(env, applicationJniObj, "cameraSwitchCallback", "(Ljava/lang/String;)V");
    if (mid == NULL)
            goto FAIL1;

    js = (*env)->NewStringUTF(env, name);
    (*env)->CallVoidMethod(env, applicationJniObj, mid, js);

    if (isAttached)
    {
        (*global_vm)->DetachCurrentThread(global_vm);
    }
    LOGE("SampleSwitchCamera End");
    return;
FAIL1:
    if (isAttached)
    {
        (*global_vm)->DetachCurrentThread(global_vm);
    }
FAIL0:
    LOGE("SampleSwitchCamera FAILED");
    return;
}

So I tried like this:

 void CallbackFromJni(const int *id)
{
jboolean isAttached;
JNIEnv *env;
jmethodID mid;
jstring js;
LOGE("callbackFromJni Begin %d", id);
env = getJniEnv(&isAttached);
if (env == NULL)
    goto FAIL0;

mid = getApplicationJniMethodId(env, applicationJniObj, "callbackFromJni", "(Ljava/lang/Integer;)V");
if (mid == NULL)
    goto FAIL1;

jintArray array = (*env)->NewIntArray(env, id);
(*env)->CallVoidMethod(env, applicationJniObj, mid, array);

if (isAttached)
{
    (*global_vm)->DetachCurrentThread(global_vm);
}
LOGE("callbackFromJni End");
return;
FAIL1:
if (isAttached)
{
    (*global_vm)->DetachCurrentThread(global_vm);
}
FAIL0:
LOGE("callbackFromJni FAILED");
return;
}

But I get this error:

01-26 11:17:36.654: E/VidyoMobile app/src/main/jni/ndkVidyoSample.c(22071): callbackFromJni Begin 101
01-26 11:17:36.655: E/art(22071): JNI ERROR (app bug): attempt to pass an instance of int[] as argument 1 to void com.vidyo.vidyocore.VidyoCoreApplication.callbackFromJni(java.lang.Integer)
01-26 11:17:36.692: A/art(22071): art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: bad arguments passed to void com.vidyo.vidyocore.VidyoCoreApplication.callbackFromJni(java.lang.Integer) (see above for details)
01-26 11:17:36.692: A/art(22071): art/runtime/java_vm_ext.cc:410]   native: #09 pc 000024a9  /data/app/be.belfius.videocall-1/lib/arm/libndkVidyoSample.so (CallbackFromJni+104)
01-26 11:17:37.219: A/art(22071): art/runtime/runtime.cc:366]   native: #12 pc 000024a9  /data/app/be.belfius.videocall-1/lib/arm/libndkVidyoSample.so (CallbackFromJni+104)
01-26 11:17:37.220: A/art(22071): art/runtime/runtime.cc:366]   native: #14 pc 000024a9  /data/app/be.belfius.videocall-1/lib/arm/libndkVidyoSample.so (CallbackFromJni+104)
01-26 11:17:37.365: A/DEBUG(195): Abort message: 'art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: bad arguments passed to void com.vidyo.vidyocore.VidyoCoreApplication.callbackFromJni(java.lang.Integer) (see above for details)'
01-26 11:17:37.389: A/DEBUG(195):     #14 pc 000024a9  /data/app/be.belfius.videocall-1/lib/arm/libndkVidyoSample.so (CallbackFromJni+104)

I am asumming cause I need an int and this is creating an intArray. How can I create an int, because there is no newInt , just newIntArray


Solution

  • you declared to get method that gets Integer object as parameter

    mid = getApplicationJniMethodId(env, applicationJniObj, "callbackFromJni", "(Ljava/lang/Integer;)V");

    but then you are passing integer table int[] as parameter

    jintArray array = (*env)->NewIntArray(env, id);
    (*env)->CallVoidMethod(env, applicationJniObj, mid, array);
    

    correct way of declaring types to use are described here: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html


    if you want to pass Integer as parameter, you need to change creating table to creating single Integer object

    jclass int_cls = (*env)->FindClass(env, "java/lang/Integer");
    if (int_cls == NULL)
        goto FAIL1;
    
    jmethodID int_constructor = (*env)->GetMethodID(env, int_cls, "<init>", "(I)V");
    if (int_constructor == NULL)
        goto FAIL1;
    
    jobject java_int = (*env)->NewObject(env, int_cls, int_constructor, (jint)*id);
    if (java_int == NULL)
        goto FAIL1;
    
    (*env)->CallVoidMethod(env, applicationJniObj, mid, java_int);
    

    method in Java side in this case need to look like:

    native void callbackFromJni(Integer result);
    

    instead of Integer it would be a lot easier to pass simple int rather than Integer as primitive types can be passed directly instead of boxing them in objects

    mid = getApplicationJniMethodId(env, applicationJniObj, "callbackFromJni", "(I)V");
    if (mid == NULL)
        goto FAIL1;
    
    (*env)->CallVoidMethod(env, applicationJniObj, mid, *id);
    

    method in Java side in this case need to look like:

    native void callbackFromJni(int result);
    

    if you were trying to pass integer array int[] you would need to declare

    mid = getApplicationJniMethodId(env, applicationJniObj, "callbackFromJni", "([I)V");
    

    parameter passed to creating new table is the size, not the array itself, so you would need to get size of that array first and use it when creating table

    jintArray array = (*env)->NewIntArray(env, size);
    

    then set array content and pass it to callback

    (*env)->SetIntArrayRegion(env, array, 0, size, id);
    (*env)->CallVoidMethod(env, applicationJniObj, mid, array);
    

    method in Java side in this case need to look like:

    native void callbackFromJni(int[] result);