在 Java 和 C 之间发送 int[]s

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4841345/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 08:17:02  来源:igfitidea点击:

Sending int[]s between Java and C

javaandroidcjava-native-interfaceandroid-ndk

提问by rbcc

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.

我在 Android 中有一些图像处理 Java 代码,它们作用于两个大型 int 数组。大多数时候,Java 足够快,但我需要通过 JNI 和 NDK 使用 C 来加速一些操作。

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.

我知道可以将数据从 int 数组传递到 C 的唯一方法是使用 ByteBuffer.allocateDirect 创建一个新缓冲区,将数据复制到该缓冲区,然后使 C 代码对缓冲区进行操作。

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?

但是,我看不到任何可以在 Java 中操作此缓冲区中的数据的方法,就好像缓冲区是 int[] 或 byte[] 一样。例如,对 ByteBuffer.array() 的调用将在新创建的缓冲区上失败。有什么办法可以使这项工作?

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.

我的内存有限,想减少我需要的数组/缓冲区的数量。例如,如果我可以使用 IntBuffer.wrap(new int[...]) 创建缓冲区,然后直接在 Java 中操作支持缓冲区的数组,那就太好了,但我不能这样做,因为唯一似乎在这里为 JNI 工作的是 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?

有没有其他方法可以在 C 和 Java 之间来回发送数据?我可以以某种方式在 C 端分配内存并让 Java 直接向那里发送数据吗?

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

编辑:将缓冲区使用与 int[] 使用进行比较的基准测试:

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.

在 Droid 手机上,缓冲版本需要约 10 秒才能完成,阵列版本需要约 0.01 秒。

回答by Brian D

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

http://java.sun.com/docs/books/jni/html/objtypes.html,使用 JNIGet/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

在这个例子中,我将传递一个数组(为了参数,它int array = new int[10]然后用 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.

学习第 3.3 节,特别是 3.3.2——这将允许您获取指向 java 内存中数组的指针,修改它,然后释放它,实际上允许您在本机代码中修改数组。

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

我刚刚在我自己的项目中使用了它(使用短数组)并且效果很好:)

回答by Jason LeBrun

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

如果您使用直接分配的缓冲区,则可以使用该GetDirectBufferAddress函数直接从 C 访问支持数组。这防止了复制该区域的区域的可能性。

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.

您可以像操作普通的 C 数组一样直接对返回的地址进行操作,它会直接修改 Java 直接分配的缓冲区。

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.

然后,作为临时状态,您可以使用 ByteBuffer.asIntBuffer() 和 family 以模拟各种 Java 原语数组的方式访问缓冲区。

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

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

回答by TheCottonSilk

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

您可以使用回调将数据从本机层发送到 Java。

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

在 Java 层:在我的本机类中,我有以下方法:

//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 */

     ...
}