2011-01-30 53 views
12

Tôi có một số mã Java xử lý hình ảnh trong Android hoạt động trên hai mảng int lớn. Hầu hết thời gian, Java đủ nhanh nhưng tôi cần sử dụng C thông qua JNI và NDK để tăng tốc một số thao tác.Gửi int [] s giữa Java và C

Cách duy nhất tôi biết rằng tôi có thể chuyển dữ liệu từ mảng int sang C là sử dụng ByteBuffer.allocateDirect để tạo bộ đệm mới, sao chép dữ liệu vào đó và sau đó làm cho mã C hoạt động trên bộ đệm.

Tuy nhiên, tôi không thể thấy bất kỳ cách nào tôi có thể thao tác dữ liệu trong bộ đệm này trong Java như thể bộ đệm là một int [] hoặc một byte []. Ví dụ, một cuộc gọi đến ByteBuffer.array() sẽ thất bại trên bộ đệm mới được tạo ra. Có cách nào để thực hiện công việc này không?

Tôi có bộ nhớ hạn chế và muốn giảm số lượng mảng/bộ đệm tôi cần. Ví dụ, nó sẽ là tốt đẹp nếu tôi có thể sử dụng IntBuffer.wrap (new int [...]) để tạo bộ đệm và sau đó thao tác mảng sao lưu bộ đệm trực tiếp trong Java nhưng tôi không thể làm điều này bởi vì điều duy nhất mà dường như làm việc ở đây cho JNI là ByteBuffer.allocateDirect.

Có cách nào khác để gửi dữ liệu qua lại giữa C và Java không? Tôi có thể bằng cách nào đó phân bổ bộ nhớ ở phía C và có Java gửi dữ liệu trực tiếp đến đó?

Edit: Một benchmark so sánh sử dụng đệm để int [] sử dụng:

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; 
    } 
} 

Trên điện thoại Droid, phiên bản đệm mất ~ 10 giây để hoàn thành và phiên bản mảng mất ~ 0,01 giây.

+1

Chờ, có chuyện gì với [ 'ByteBuffer.asIntBuffer'] (http://developer.android.com/reference/java/nio/ByteBuffer.html#asIntBuffer%28%29)? Bạn có thực sự cần phải có một 'int []'? – ephemient

+0

... và hơn nữa, bạn có thể gọi .array() trên IntBuffer nếu bạn muốn. – mpontillo

+0

.array() ném một ngoại lệ không được hỗ trợ cho cả ByteBuffer và IntBuffer cho các mảng được cấp phát trực tiếp trong Java. Tôi thực sự muốn truy cập vào int [] để tôi có thể thực hiện một số công cụ xử lý hình ảnh ít tốn kém hơn trong Java, ví dụ: chuyển đổi một hình ảnh thành màu đen và trắng bằng cách kiểm tra các pixel nào nằm trên/dưới ngưỡng sáng. Sử dụng IntBuffer get/put cho mỗi pixel sẽ quá chậm trong Android cũng như vụng về. – rbcc

Trả lời

17

Từ http://java.sun.com/docs/books/jni/html/objtypes.html, sử dụng JNI của Get/Release<TYPE>ArrayElements(...)

Trong ví dụ này, tôi sẽ chuyển một mảng (vì lợi ích của đối số, đó là int array = new int[10] và sau đó điền nó với 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; 
} 

Phần nghiên cứu 3.3 và cụ thể 3.3.2 - điều này sẽ cho phép bạn đưa con trỏ đến mảng trong bộ nhớ của java, sửa đổi và phát hành nó, có hiệu lực cho phép bạn sửa đổi mảng trong mã gốc .

Tôi vừa mới sử dụng nó trong dự án của riêng tôi (với mảng ngắn) và nó hoạt động tuyệt vời :)

+0

nhờ mã này đã giúp tôi kết nối với phương pháp thư viện siglib cho bộ lọc FIR – JPM

+2

để có được số int arrayLength = (* env) -> GetArrayLength (env, arr); –

+0

Khi tôi thử điều này, Java doStuffArray (arr) trả về trước khi Java_IntArray_doStuffArray nguyên gốc hoàn thành. Tôi cần phải làm gì để chặn bên java cho đến khi bên gốc hoàn thành? –

3

Bạn có thể sử dụng gọi lại để gửi dữ liệu từ lớp gốc sang Java.

Trong lớp Java: trong lớp mẹ đẻ của tôi tôi có phương pháp sau đây:

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

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

    ... 

} 

Trong lớp Quê quán: trong việc thực hiện mẹ đẻ của tôi:

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

    ... 
} 
+0

Bạn có thể xin đề cập, loại nào là 'jc'? Có phải là 'char * jc' không? –

+0

@dma_k: Trong trường hợp của tôi 'jc' là một mảng có kích thước đã biết. – TheCottonSilk

+0

Bạn có thể đưa ra gợi ý, làm cách nào để bạn hoạt động với 'jc' (ví dụ: đặt dữ liệu ký tự)? Tôi nhìn ở đây và ở đó cho các ví dụ về cách sử dụng 'SetCharArrayRegion()', nhưng không thể tìm thấy một ví dụ hoàn chỉnh về cách tạo/khởi tạo một mảng 'jchar' (ví dụ, từ chuỗi 1 byte ASCII, 'char * '). Cảm ơn –

5

Nếu bạn đang sử dụng bộ đệm được phân bổ trực tiếp, bạn có thể truy cập vào mảng ủng hộ trực tiếp từ C, sử dụng Hàm GetDirectBufferAddress. Điều này ngăn cản khả năng sao chép các vùng của khu vực.

Bạn có thể hoạt động trên địa chỉ trả về trực tiếp như bạn sẽ là một mảng C bình thường, và nó sẽ trực tiếp sửa đổi bộ đệm được cấp phát trực tiếp Java.

Sau đó, dưới dạng trạng thái tạm thời, bạn có thể sử dụng ByteBuffer.asIntBuffer() và gia đình để truy cập bộ đệm theo cách mô phỏng các mảng của các nguyên bản Java khác nhau.

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

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