我有一个用C ++编写的JNI函数,它接受一个字节数组作为输入,对其进行分段,并将一个字节数组返回给Java。
JNIEXPORT jobjectArray JNICALL Java_class_method(JNIEnv * env,jobject o,jbyteArray dataToSegment);
在Java方面,它很简单:
byte[] arg = getRandomByteArray();
Object[] retVal = x.method(arg);
现在,我发现JNI部分有点棘手。我打算创建一个对象数组,每个对象都是一个字节数组。这是因为JNI只定义了有限数量的Java类型。有一个jbyteArray类型和一个jobjectArray类型,但没有jarrayOfByteArrays类型。
所以我创建了我的对象数组,每个对象被初始化为一个新的字节[1024]:
jobjectArray retVal = env->NewObjectArray(numSegs, env->FindClass("[Ljava/lang/Object;"), env->NewByteArray(1024));
然后迭代遍历此数组中的所有索引,执行以下操作:
jbyteArray tmp = (jbyteArray) env->GetObjectArrayElement(retVal, i);
env->SetByteArrayRegion(tmp, 0, size, (jbyte*) sourceBuffer);
env->SetObjectArrayElement(retVal, i, (jobject) tmp); // <--- Questionable line
在大多数情况下,一切都很好。但是,如果我希望每个字节数组都是可变长度怎么办?也就是说,我希望字节数组的数组“锯齿状”。作为初始值,我作为NewObjectArray()的最后一个参数传递什么?我已经尝试传递0作为初始值以防止在创建jobjectArray时初始化,然后分配新的jbyteArray对象以传递给SetObjectArrayElement(),但这只会在每次我尝试调用SetObjectArrayElement时抛出ArrayStoreException。实际上,即使为tmp对象(而不是GetObjectArrayElement()中的一个)分配新的jbyteArray,也会导致在调用SetObjectArrayElement()时抛出相同的异常。有没有理由说最后一行代码会成为一个问题?是否无法使用jbyteArray作为参数调用SetObjectArrayElement()?
经过进一步检查,似乎“可疑线”根本没有做任何事情。当我评论它时,对tmp所做的任何更改都会反映在retVal中。我理解这是因为SetByteArrayRegion调用正在处理jobjectArray中的“in”字节数组,而不是副本。如果所有行(相反,所有单维字节数组)的长度相同,那对我来说就足够了。但他们不是。如何将新字节数组分配给此对象数组中的一行?
TL; DR: 使用JNI,我有一个jobjectArray的jbyteArrays。如何用一个用NewByteArray创建的新jbyteArray替换其中一个jbyteArrays?提示:env-&gt; SetObjectArrayElement(retVal,i,(jobject)env-&gt; NewByteArray(size)); //不起作用。
答案 0 :(得分:3)
我刚刚通过JNI成功返回了字节数组的数组。初始大小与字节数组无关,因为在填充jobject数组时用新的替换它:
static jbyteArray NewJavaStringBytes(JNIEnv* env, const char *src) {
jbyteArray retVal = (*env)->NewByteArray(env, strlen(src));
jbyte *buf = (*env)->GetByteArrayElements(env, retVal, NULL);
strcpy(buf, src);
printf(" NewJavaStringBytes: Created java byte array: %s.\n", buf);
(*env)->ReleaseByteArrayElements(env, retVal, buf, 0);
return retVal;
}
JNIEXPORT jobjectArray JNICALL Java_TestJniString_printStringArray
(JNIEnv *env, jobject thisObj, jobjectArray jObjArr) {
int numStr = (*env)->GetArrayLength(env, jObjArr);
int idx = 0;
jobjectArray strArr = NULL;
jbyte *curStr = NULL;
jclass arrayElemType = (*env)->FindClass(env, "[B");
const char *retStrs[] = {"one", "two", "three", "twenty-five", "TESTING!!"};
const int RET_LEN = sizeof(retStrs) / sizeof(char *);
printf("Creating java object array of %d items.\n", RET_LEN);
//Create new array of byte array
jobjectArray testArray = (*env)->NewObjectArray(env,
RET_LEN,
arrayElemType,
(*env)->NewByteArray(env, 1) );
for (idx = 0; idx < RET_LEN; ++idx) {
printf("Creating java byte array %d from str: %s.\n", idx, retStrs[idx]);
jbyteArray str = NewJavaStringBytes(env, retStrs[idx]);
(*env)->SetObjectArrayElement(env, testArray, idx, str);
(*env)->DeleteLocalRef(env, str);
}
printf("printStringArray: Printing %d strings:\n", numStr);
for (idx = 0; idx < numStr; ++idx) {
strArr = (*env)->GetObjectArrayElement(env, jObjArr, idx);
curStr = (*env)->GetByteArrayElements(env, strArr, NULL);
printf(" %s.\n", curStr);
(*env)->ReleaseByteArrayElements(env, (jbyteArray)strArr, curStr, 0);
}
(*env)->DeleteGlobalRef(env, arrayElemType);
return testArray;
}
此示例接受一个字节数组数组并返回一个字节数组数组。注意这是在C(而不是C ++)中所以jni调用是(* env) - &gt;(env,...);.在C ++中,包装器简化了调用。另外,请注意,这假定java代码在发送到本机层之前在字符串的字节数组版本上添加空终止符。如果您不能指望它,则必须在C / C ++代码中手动添加空术语,因为从String转换时Java不会对byte []执行此操作。
希望有所帮助。
答案 1 :(得分:2)
如果您可以选择使用JNA而不是JNI,那么您应该可以使用与JNA API文档页面中提供的示例类似的内容:
http://jna.java.net/javadoc/overview-summary.html#arrays
“要映射本机多维数组,请使用具有与完整本机数组等效的多个元素的一维Java数组”