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

在Java和C之间发送int[]s,java,android,c,java-native-interface,android-ndk,Java,Android,C,Java Native Interface,Android Ndk,我有一些Android中的图像处理Java代码,作用于两个大型int数组。大多数情况下,Java速度足够快,但我需要通过JNI和NDK使用C来加速一些操作 我知道可以将数据从int数组传递到C的唯一方法是使用ByteBuffer.allocateDirect创建一个新的缓冲区,将数据复制到该缓冲区,然后使C代码作用于缓冲区 但是,我看不出有任何方法可以像处理int[]或byte[]一样在Java中操作该缓冲区中的数据。例如,对ByteBuffer.array()的调用将在新创建的缓冲区上失败。有

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

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

但是,我看不出有任何方法可以像处理int[]或byte[]一样在Java中操作该缓冲区中的数据。例如,对ByteBuffer.array()的调用将在新创建的缓冲区上失败。有什么办法可以让这一切顺利进行吗

我的内存有限,希望减少所需的阵列/缓冲区数量。例如,如果我可以使用IntBuffer.wrap(new int[…])来创建缓冲区,然后在Java中直接操作支持缓冲区的数组,那就太好了,但我不能这样做,因为在JNI中唯一有效的方法是ByteBuffer.allocateDirect

在C和Java之间来回发送数据还有其他方法吗?我可以在C端分配内存,让Java直接向C端发送数据吗

编辑:比较缓冲区使用与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;
  }
}
int size=1000;
IntBuffer allocateDirect=java.nio.ByteBuffer.allocateDirect(4*size).asIntBuffer();
对于(int i=0;i<100;++i)
{
用于(int x=0;x

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

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

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

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

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

    ...

}
在本机层中:在我的本机实现中:

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

     ...
}
从中,使用JNI的
Get/ReleaseArrayElements(…)

在本例中,我将传递一个数组(为了参数起见,它是
intarray=newint[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;
 }
JNIEXPORT jint JNICALL
Java_IntArray_dostufvarray(JNIEnv*env、jobject obj、jintArray arr)
{
//初始化、声明等
jint*c_阵列;
jint i=0;
//获取指向数组的指针
c_数组=(*env)->GetIntArrayElements(env,arr,NULL);
//做一些异常检查
if(c_数组==NULL){
返回-1;/*发生异常*/
}
//对数组执行一些操作
对于(i=0;iReleaseIntarrayements(环境、阵列、c_阵列,0);
//还不还,由你决定
返回0;
}
学习第3.3节,特别是第3.3.2节——这将允许您在java内存中获取指向数组的指针,修改并释放它,实际上允许您在本机代码中修改数组


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

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

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

然后,如ephemient所述,您可以使用ByteBuffer.asIntBuffer()和family以模拟各种Java原语数组的方式访问缓冲区


等等,怎么了?你真的需要一个
int[]
?…而且,如果你愿意,你可以在IntBuffer上调用.array()。.array()对Java中直接分配的数组的ByteBuffer和IntBuffer都抛出一个不受支持的异常。我真的想要访问int[]因此,我可以用Java做一些不太密集的图像处理工作,例如,通过检查哪些像素高于/低于亮度阈值,将图像转换为黑白。在Android中,为每个像素使用IntBuffer get/put会太慢,而且会很笨拙。@rebeccaT,将代码改为使用IntBuffer而不是int[]并直接使用buffers@rebeccaT,如果put/get速度太慢,则会出现严重错误。这些方法应该内联并编译为非常简单的CPU指令,而不需要任何虚拟方法调用。是的,我建议您转储int[]始终使用direct IntBuffer。请您提一下,什么类型是
jc
?它是
char*jc
?@dma_k:在我的例子中,“jc”是一个已知大小的数组。您能给我一个提示吗,您如何使用
jc
(例如设置字符数据)?我到处寻找使用
SetCharArrayRegion()的示例
,但找不到如何创建/初始化
jchar
s数组(例如,从ASCII 1字节字符串,
char*
)的完整示例。感谢感谢感谢此代码帮助我连接到FIR过滤器的siglib库方法,以获取计数int-arrayLength=(*env)->GetArrayLength(env,arr);尝试此操作时,Java DostufArray(arr)会在本机Java_IntArray_dostufArray完成之前返回。在本机端完成之前,我需要如何阻止Java端?