2013-08-06 31 views
7

Tôi có hàm JNI, được viết bằng C++, lấy một mảng byte làm đầu vào, phân đoạn và trả về mảng mảng byte cho Java.Tạo một byte [] [] trong C++ và trả về Java bằng JNI

JNIEXPORT jobjectArray JNICALL Java_class_method (JNIEnv * env, jobject o, jbyteArray dataToSegment);

Về phía Java, đó là một cái gì đó đơn giản như:

byte[] arg = getRandomByteArray(); 
Object[] retVal = x.method(arg); 

Bây giờ, tôi đang tìm kiếm một phần JNI để có một chút khéo léo. Tôi dự định tạo một mảng các đối tượng, mỗi đối tượng trong số đó là một mảng byte. Điều này là do JNI chỉ định nghĩa một số loại Java giới hạn. Có một kiểu jbyteArray và một kiểu jobjectArray, nhưng không có kiểu jarrayOfByteArrays.

Vì vậy, tôi tạo ra mảng của tôi về các đối tượng, mỗi đối tượng được khởi tạo như một byte mới [1024]:

jobjectArray retVal = env->NewObjectArray(numSegs, env->FindClass("[Ljava/lang/Object;"), env->NewByteArray(1024)); 

sau đó tôi lặp trên tất cả các chỉ số trong mảng này, làm cái gì đó như:

jbyteArray tmp = (jbyteArray) env->GetObjectArrayElement(retVal, i); 
env->SetByteArrayRegion(tmp, 0, size, (jbyte*) sourceBuffer); 
env->SetObjectArrayElement(retVal, i, (jobject) tmp); // <--- Questionable line 

và mọi thứ hoạt động tuyệt vời, phần lớn. Tuy nhiên, nếu tôi muốn mỗi mảng byte có độ dài thay đổi thì sao? Đó là, tôi muốn mảng mảng byte được "lởm chởm". Tôi làm gì để truyền tham số cuối cùng cho NewObjectArray() làm giá trị ban đầu? Tôi đã thử vượt qua 0 là giá trị ban đầu để ngăn chặn khởi tạo tại thời điểm tạo ra jobjectArray, và sau đó phân bổ các đối tượng jbyteArray mới để truyền cho SetObjectArrayElement(), nhưng điều này sẽ chỉ kết thúc ném ArrayStoreException mỗi khi tôi cố gắng gọi SetObjectArrayElement. Thật vậy, thậm chí gán một jbyteArray mới cho đối tượng tmp (thay vì một từ GetObjectArrayElement()) kết quả trong cùng một ngoại lệ được ném ra khi SetObjectArrayElement() được gọi. Có một lý do tại sao dòng cuối cùng của mã sẽ là một vấn đề? Không thể gọi SetObjectArrayElement() với một jbyteArray như một tham số?

Dường như, khi kiểm tra kỹ hơn, rằng "Dòng có vấn đề" không làm gì cả. Khi tôi nhận xét nó ra, bất kỳ thay đổi được thực hiện để tmp được phản ánh trong retVal. Tôi hiểu điều này là bởi vì các cuộc gọi SetByteArrayRegion là đối phó với mảng byte đó là "trong" jobjectArray, và không phải là một bản sao. Điều đó sẽ đủ cho tôi nếu tất cả các hàng (thay vào đó, tất cả các mảng byte một chiều) đều có cùng độ dài. Nhưng họ không. Làm thế nào để gán một mảng byte mới cho một trong các hàng trong mảng đối tượng này?

TL; DR: Với JNI, tôi có một jobjectArray của jbyteArrays. Làm thế nào để thay thế một trong các jbyteArrays bằng một cái mới được tạo ra với NewByteArray? Gợi ý: env-> SetObjectArrayElement (retVal, i, (jobject) env-> NewByteArray (kích thước)); // không hoạt động.

Trả lời

2

Nếu bạn có tùy chọn để sử dụng JNA thay vì JNI, bạn sẽ có thể sử dụng một cái gì đó tương tự với ví dụ được cung cấp trong trang tài liệu API JNA:

http://jna.java.net/javadoc/overview-summary.html#arrays

"Để ánh xạ một người gốc mảng đa chiều, sử dụng mảng Java một chiều với một số phần tử tương đương với toàn bộ mảng gốc "

+0

Cảm ơn, nhưng tiếc là tôi bị kẹt với JNI ở đây. –

+0

@SzymonSmakolski sử dụng NewDirectByteBuffer từ mã JNI để cấp phát bộ đệm. – Eugene

+0

Dựa trên sự hiểu biết [hạn chế] của tôi, NewDirectByteBuffer hoạt động với bộ nhớ được cấp phát ở phía gốc. Tôi đang tìm cách để JVM phân bổ một loạt các đối tượng Java byte [] mà tôi có thể nhồi nhét vào một đối tượng []. Cách tiếp cận bạn đề xuất sẽ cho tôi phân bổ từng mảng byte bằng toán tử mới của C++, chứ không phải JVM. Tôi muốn cho JVM xử lý việc phân bổ cho bất kỳ dữ liệu nào thực sự kết thúc ở phía Java. –

3

Tôi vừa trả về mảng mảng byte thành công thông qua JNI.Kích thước ban đầu không quan trọng của mảng byte của bạn bởi vì bạn thay thế nó bằng một cái mới như bạn cư mảng 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; 
} 

dụ này có một mảng của mảng byte và trả về một mảng của mảng byte. Lưu ý đây là trong C (không phải C++) để các cuộc gọi jni là (* env) -> (env, ...) ;. Trong C++, trình bao bọc đơn giản hóa các cuộc gọi. Ngoài ra, LƯU Ý rằng điều này giả định mã java thêm null terminator trên phiên bản mảng byte của chuỗi trước khi gửi đến lớp gốc. Nếu bạn không thể dựa vào đó, thì bạn phải tự thêm thuật ngữ rỗng trong mã C/C++ vì Java sẽ không làm điều này cho byte [] khi chuyển đổi từ String.

Hy vọng điều đó sẽ hữu ích.

Các vấn đề liên quan