Sending int[]s between Java and C

13,872

Solution 1

From http://java.sun.com/docs/books/jni/html/objtypes.html, use JNI's Get/Release<TYPE>ArrayElements(...)

In this example, I will pass an array ( for argument's sake, it's int array = new int[10] and then fill it with 0-9

 JNIEXPORT jint JNICALL 
 Java_IntArray_doStuffArray(JNIEnv *env, jobject obj, jintArray arr)
 {

     // initializations, declarations, etc
     jint *c_array;
     jint i = 0;

     // get a pointer to the array
     c_array = (*env)->GetIntArrayElements(env, arr, NULL);

     // do some exception checking
     if (c_array == NULL) {
         return -1; /* exception occurred */
     }

     // do stuff to the array
     for (i=0; i<10; i++) {
         c_array[i] = i;
     }

     // release the memory so java can have it again
     (*env)->ReleaseIntArrayElements(env, arr, c_array, 0);

     // return something, or not.. it's up to you
     return 0;
 }

Study section 3.3, and specifically 3.3.2 -- this will allow you to get a pointer to the array in java's memory, modify it, and release it, in effect allowing you to modify the array in native code.

I've just used it in my own project (with short arrays) and it works great :)

Solution 2

If you're using direct allocated buffers, you can access the backing array directly from C, using the GetDirectBufferAddress function. This prevents the possibility of copying regions of the area.

You can operate on the returned address directly as you would a normal C array, and it will directly modify the Java direct-allocated buffer.

Then, as ephemient states, you can use ByteBuffer.asIntBuffer() and family to access the buffer in a way that emulates arrays of the various Java primitives.

http://download.oracle.com/javase/1.4.2/docs/guide/jni/jni-14.html

Solution 3

You can use callback for sending the data from native layer to Java.

In Java layer: in my native class I have following methods:

//Native method
public native String getStrData(int size);

//Callback method
public void addData(char[] native_data, int size) {

    ...

}

In Native layer: in my native implementation:

JNIEXPORT jstring JNICALL Java_com_pkg_NativeClass_getStrData 
   (JNIEnv *env, jobject obj, jint size) {
     ...

     jclass native_class;           /* Callback: native class */
     jmethodID native_method_id;    /* Callback: native method id */
     jcharArray row;                /* Callback: native data */

     ...

    /* Start Callback: Native to Java  */   
    native_class = (*env)->GetObjectClass(env, obj);
    native_method_id = (*env)->GetMethodID(env, native_class, "addData", "([CI)V");
    if (native_method_id == 0) {
        return (jstring)ERR_NATIVE_METHODID;
    }
    row = (jcharArray)(*env)->NewCharArray(env, size);
    /* jc has the data to be sent to Java */
    (*env)->SetCharArrayRegion(env, (jcharArray)row, (jsize)0, size, (jchar *)jc);

    (*env)->CallVoidMethod(env, obj, native_method_id, row, size);
    /* End Callback */

     ...
}
Share:
13,872
rbcc
Author by

rbcc

Updated on June 04, 2022

Comments

  • rbcc
    rbcc almost 2 years

    I have some image processing Java code in Android that acts upon two large int arrays. Most of the time, Java is fast enough but I need to use C via JNI and the NDK to speed up a few operations.

    The only way I know that I can pass the data from the int arrays to C is to use ByteBuffer.allocateDirect to create a new buffer, copy the data to that and then make the C code act upon the buffer.

    However, I cannot see any way I can manipulate the data in this buffer in Java as if the buffer was an int[] or a byte[]. For example, a call to ByteBuffer.array() will fail on the newly created buffer. Is there any way to make this work?

    I have limited memory and want to reduce how many arrays/buffers I need. For example, it would be nice if I could use IntBuffer.wrap(new int[...]) to create the buffer and then manipulate the array backing the buffer directly in Java but I cannot do this because the only thing that seems to work here for JNI is ByteBuffer.allocateDirect.

    Are there any other ways to send data back and forth between C and Java? Can I somehow allocate memory on the C side and have Java send data directly to there?

    Edit: A benchmark comparing buffer use to int[] use:

    int size = 1000;
    IntBuffer allocateDirect = java.nio.ByteBuffer.allocateDirect(4 * size).asIntBuffer();
    for (int i = 0; i < 100; ++i)
    {
      for (int x = 0; x < size; ++x)
      {
        int v = allocateDirect.get(x);
        allocateDirect.put(x, v + 1);
      }
    }
    
    int[] intArray = new int[size];
    for (int i = 0; i < 100; ++i)
    {
      for (int x = 0; x < size; ++x)
      {
        int v = intArray[x];
        intArray[x] = v + 1;
      }
    }
    

    On a Droid phone, the buffer version takes ~10 seconds to finish and the array version takes ~0.01 seconds.