2011-01-30 13 views
12

Ho un codice Java di elaborazione immagini in Android che agisce su due grandi array int. La maggior parte delle volte, Java è abbastanza veloce ma ho bisogno di usare C tramite JNI e NDK per velocizzare alcune operazioni.Invio di int [] s tra Java e C

L'unico modo per sapere che posso passare i dati dalle int arrays a C è utilizzare ByteBuffer.allocateDirect per creare un nuovo buffer, copiare i dati su quello e quindi fare in modo che il codice C agisca sul buffer.

Tuttavia, non riesco a vedere alcun modo per manipolare i dati in questo buffer in Java come se il buffer fosse un int [] o un byte []. Ad esempio, una chiamata a ByteBuffer.array() non riuscirà nel buffer appena creato. C'è un modo per farlo funzionare?

Ho una memoria limitata e voglio ridurre il numero di matrici/buffer di cui ho bisogno. Ad esempio, sarebbe bello se potessi usare IntBuffer.wrap (new int [...]) per creare il buffer e quindi manipolare l'array che supporta il buffer direttamente in Java, ma non posso farlo perché l'unica cosa che sembra lavoro qui per JNI è ByteBuffer.allocateDirect.

Esistono altri modi per inviare dati avanti e indietro tra C e Java? Posso in qualche modo allocare memoria sul lato C e avere Java inviare i dati direttamente lì?

Edit: Un punto di riferimento a confronto l'uso di buffer a int [] Uso:

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

Su un telefono Droid, la versione di buffer richiede circa 10 secondi per terminare e la versione di serie prende ~ 0.01 secondi.

+1

Aspetta, cosa c'è di sbagliato con [ 'ByteBuffer.asIntBuffer'] (http://developer.android.com/reference/java/nio/ByteBuffer.html#asIntBuffer%28%29)? Hai davvero bisogno di avere un 'int []'? – ephemient

+0

... e inoltre, puoi chiamare .array() su IntBuffer se lo desideri. – mpontillo

+0

.array() genera un'eccezione non supportata sia per ByteBuffer che per IntBuffer per gli array allocati direttamente in Java. Voglio davvero accedere a un int [] in modo da poter eseguire alcuni dei processi di elaborazione delle immagini meno intensi in Java, ad es. convertire un'immagine in bianco e nero controllando quali pixel sono sopra/sotto una soglia di luminosità. L'utilizzo di IntBuffer get/put per ogni pixel sarebbe troppo lento in Android oltre ad essere goffo. – rbcc

risposta

17

Da http://java.sun.com/docs/books/jni/html/objtypes.html, utilizzare di JNI Get/Release<TYPE>ArrayElements(...)

In questo esempio, passerò un array (per argomento, è int array = new int[10] e quindi lo riempiamo con 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; 
} 

sezione Studio 3.3, e in particolare 3.3.2 - questo vi permetterà di ottenere un puntatore alla matrice nella memoria di java, modificarlo, e rilasciarlo, in effetti che consente di modificare la matrice in codice nativo .

ho appena usato nel mio progetto (con array brevi) e funziona benissimo :)

+0

grazie a questo codice mi ha aiutato a connettermi al metodo libreria siglib per i filtri FIR – JPM

+2

per ottenere il conteggio int arrayLength = (* env) -> GetArrayLength (env, arr); –

+0

Quando provo a farlo, il Java doStuffArray (arr) ritorna prima del completamento del Java_IntArray_doStuffArray nativo. Cosa devo fare per bloccare il lato java fino al completamento del lato nativo? –

3

È possibile utilizzare la richiamata per inviare i dati dal livello nativo a Java.

In strato di Java: nella mia classe nativa ho seguenti metodi:

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

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

    ... 

} 

nello strato mie origini: nella mia implementazione nativa:

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

Potresti per favore menziona, che tipo è 'jc'? E ''char * jc'? –

+0

@dma_k: Nel mio caso 'jc' era una matrice di dimensioni note. – TheCottonSilk

+0

Potete dare un suggerimento, come operate con 'jc' (ad esempio, impostate i dati dei caratteri)? Guardo qua e là per esempi di utilizzo di 'SetCharArrayRegion()', ma non riesco a trovare un esempio completo di come creare/inizializzare una matrice di 'jchar's (ad esempio, da una stringa ASCII a 1 byte,' char * '). Grazie –

5

Se stai usando buffer allocati diretti, è possibile accedere alla matrice di supporto direttamente da C, utilizzando il GetDirectBufferAddress funzione. Ciò impedisce la possibilità di copiare regioni dell'area.

È possibile operare sull'indirizzo restituito direttamente come si farebbe con un normale array C, e modificherà direttamente il buffer assegnato direttamente da Java.

Quindi, come stati effeti, è possibile utilizzare ByteBuffer.asIntBuffer() e la famiglia per accedere al buffer in un modo che emula gli array delle varie primitive Java.

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

Problemi correlati