4

I'm having trouble finding the right documentation for passing a char buffer from JNI method to Java method. Here's the code

jint JNICALL Java_foo_package_MyJavaClass_myNativeMethod(JNIEnv *jenv, jobject jobj)
{
    jclass clazz = (*jenv)->GetObjectClass(jenv, jobj);
    //  MyJavaClass method:  private void addData(byte[] data)
    jmethodID mid = (*jenv)->GetMethodID(jenv, clazz, "addData", "([B)V");
    assert(mid);

    const char buf[] = { 0, 1, 2, 3, 42 };
    const size_t buf_len = sizeof buf;

    (*jenv)->CallVoidMethod(jenv, jobj, mid, buf /* obviously wrong */ );

    return 0;
}

Is CallVoidMethod the right function to use here, what's the correct thing to pass to it, how to allocate it, and how (if at all) it should be freed?

A code snippet would probably be the most compact answer, with a few words explaining how ownership of objects goes.

3
  • I think you have to change the GetMethodID param type to "([C)V". Commented Jun 6, 2014 at 8:53
  • 2
    @Yohji No, C is Java char, which is 16 bits and represents a unicode character. C char represents a native byte (8 bits assumed in this case), often (but not here) used to represent 8 bit character of any 8 bit encoding. Commented Jun 6, 2014 at 13:45
  • 1
    @hyde Java char holds a UTF-16 code-unit; One or two of which represents a Unicode character. Character.toChars(int) provides a succinct explanation. Commented Jun 7, 2014 at 3:04

2 Answers 2

4

Below example works for passing char[] from C code to Java byte[].

void JNICALL Java_com_example_testapplication_MainActivity_getJNIByteArrayArg(JNIEnv    *jenv, jobject jobj)
{
jclass clazz = (*jenv)->FindClass(jenv, "com/example/testapplication/MainActivity"); // class path
jmethodID mid = (*jenv)->GetMethodID(jenv, clazz, "addData", "([B)V");// function name

jbyteArray retArray;
char data[] = {'a','b',3,4,5};
int data_size = 5;
if(!retArray)
retArray = (*jenv)->NewByteArray(jenv, data_size);

if((*jenv)->GetArrayLength(jenv, retArray) != data_size)
{
    (*jenv)->DeleteLocalRef(jenv, retArray);
    retArray = (*jenv)->NewByteArray(jenv, data_size);
}

void *temp = (*jenv)->GetPrimitiveArrayCritical(jenv, (jarray)retArray, 0);
memcpy(temp, data, data_size);
(*jenv)->ReleasePrimitiveArrayCritical(jenv, retArray, temp, 0);

(*jenv)->CallVoidMethod(jenv, jobj, mid, retArray);
}
public void addData(byte[] data) {
    System.out.println("Buyya: From C: " + new String(data));
}
Sign up to request clarification or add additional context in comments.

Comments

3

The functions you are looking for are GetByteArrayElements and ReleaseByteArrayElements.

Something like this should do the trick:

jint JNICALL Java_foo_package_MyJavaClass_myNativeMethod(JNIEnv *jenv, jobject jobj)
{
    jclass clazz = (*jenv)->GetObjectClass(jenv, jobj);
    //  MyJavaClass method:  private void addData(byte[] data)
    jmethodID mid = (*jenv)->GetMethodID(jenv, clazz, "addData", "([B)V");
    assert(mid);

    const char buf[] = { 0, 1, 2, 3, 42 };
    const size_t buf_len = sizeof buf;

    jboolean isCopy;
    jbyte *jbuf = (*jenv)->GetByteArrayElements(jenv, buf, &isCopy);

    (*jenv)->CallVoidMethod(jenv, jobj, mid, jbuf);

    (*jenv)->ReleaseByteArrayElements(jenv, buf, jbuf, 0);

    return JNI_OK;
}

2 Comments

Why the buf_len is not used? I need to pass the length of the array to Java too?
Is is not really needed for this example, and I probably forgot to delete if from whatever I copy pasted this snippet from. You will need it for dealing with the C <=> Java conversions. I am not sure what happens java overflows buf. I would suspect you get null back if isCopy is not set and and JNI_TRUE for isCopy if it is a pointer. Below ShivBuy has a good example of where buf_len is needed in conjunction with NewByteArray().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.