Jak zwrócić tablicę z JNI do Javy?

133

Próbuję użyć Android NDK.

Czy istnieje sposób na zwrócenie tablicy (w moim przypadku int[]) utworzonej w JNI do Javy? Jeśli tak, podaj krótki przykład funkcji JNI, która to zrobi.

-Dzięki

RyanCheu
źródło

Odpowiedzi:

120

Jeśli po zapoznaniu się z dokumentacją nadal masz pytania, które powinny stanowić część Twojego początkowego pytania. W tym przypadku funkcja JNI w przykładzie tworzy kilka tablic. Tablica zewnętrzna składa się z tablicy „Object” tworzonej za pomocą funkcji JNI NewObjectArray(). Z punktu widzenia JNI, to wszystko dwuwymiarowa tablica to tablica obiektów zawierająca szereg innych wewnętrznych tablic.

Poniższa pętla for tworzy wewnętrzne tablice typu int [] przy użyciu funkcji JNI NewIntArray(). Jeśli chcesz po prostu zwrócić jednowymiarową tablicę liczb całkowitych, to NewIntArray()funkcja jest tym, czego użyjesz do utworzenia wartości zwracanej. Jeśli chcesz utworzyć jednowymiarową tablicę ciągów, użyłbyś NewObjectArray()funkcji, ale z innym parametrem dla klasy.

Ponieważ chcesz zwrócić tablicę int, twój kod będzie wyglądał mniej więcej tak:

JNIEXPORT jintArray JNICALL Java_ArrayTest_initIntArray(JNIEnv *env, jclass cls, int size)
{
 jintArray result;
 result = (*env)->NewIntArray(env, size);
 if (result == NULL) {
     return NULL; /* out of memory error thrown */
 }
 int i;
 // fill a temp structure to use to populate the java int array
 jint fill[size];
 for (i = 0; i < size; i++) {
     fill[i] = 0; // put whatever logic you want to populate the values here.
 }
 // move from the temp structure to the java structure
 (*env)->SetIntArrayRegion(env, result, 0, size, fill);
 return result;
}
Jherico
źródło
Tak, już to zrobiłem. Miałem kłopoty ze zrozumieniem przykładu, który był związany z moim problemem (ostatni) i zastanawiałem się, czy ktoś nie miałby nic przeciwko wyjaśnieniu prostszego przykładu, zwracając po prostu int [].
RyanCheu
EDYCJA: Proszę zignorować mój poprzedni komentarz, powyższy kod działa. Dziękuję Ci! To było bardzo pomocne.
RyanCheu
3
EDIT2: Kod działa, ale musisz zmienić tmp w SetIntArrayRegion (...), aby wypełnić.
RyanCheu
41

gdyby ktoś chciał wiedzieć, jak zwrócić tablicę String []:

kod java

private native String[] data();

natywny eksport

JNIEXPORT jobjectArray JNICALL Java_example_data() (JNIEnv *, jobject);

kod natywny

  JNIEXPORT jobjectArray JNICALL   
               Java_example_data  
  (JNIEnv *env, jobject jobj){  

    jobjectArray ret;  
    int i;  

    char *message[5]= {"first",   
                       "second",   
                       "third",   
                       "fourth",   
                       "fifth"};  

    ret= (jobjectArray)env->NewObjectArray(5,  
         env->FindClass("java/lang/String"),  
         env->NewStringUTF(""));  

    for(i=0;i<5;i++) {  
        env->SetObjectArrayElement(  
        ret,i,env->NewStringUTF(message[i]));  
    }  
    return(ret);  
  }  

z linku: http://www.coderanch.com/t/326467/java/java/Returning-String-array-program-Java

zajac.m2
źródło
0

Na podstawie zadanego pytania wyjaśniono to już w pierwszej odpowiedzi, w jaki sposób możemy przekazać int [] za pośrednictwem jobjectArray. Ale oto przykład, jak możemy zwrócić jobjectArray, który zawiera listy danych. Może to być pomocne na przykład w sytuacjach, gdy ktoś musi zwrócić dane w formacie 2D, aby narysować linię z punktami x i y. Poniższy przykład pokazuje, jak jobjectArray może zwracać dane w postaci następującego formatu:

Dane wejściowe Java do JNI:
Array [ Arraylistof x float points] [ Arraylistof y float points]

Wyjście JNI do java:
jobjectArray[ Arraylistz x punktów zmiennoprzecinkowych] [ Arraylistz y punktów zmiennoprzecinkowych]

    extern "C" JNIEXPORT jobjectArray JNICALL
        _MainActivity_callOpenCVFn(
                JNIEnv *env, jobject /* this */,
                jobjectArray list) {

         //Finding arrayList class and float class(2 lists , one x and another is y)
            static jclass arrayListCls = static_cast<jclass>(env->NewGlobalRef(env->FindClass("java/util/ArrayList")));
            jclass floatCls = env->FindClass("java/lang/Float");
         //env initialization of list object and float
            static jmethodID listConstructor = env->GetMethodID(arrayListCls, "<init>", "(I)V");
            jmethodID alGetId  = env->GetMethodID(arrayListCls, "get", "(I)Ljava/lang/Object;");
            jmethodID alSizeId = env->GetMethodID(arrayListCls, "size", "()I");
            static jmethodID addElementToList = env->GetMethodID(arrayListCls, "add", "(Ljava/lang/Object;)Z");

            jmethodID floatConstructor = env->GetMethodID( floatCls, "<init>", "(F)V");
            jmethodID floatId = env->GetMethodID(floatCls,"floatValue", "()F");


        //null check(if null then return)
        if (arrayListCls == nullptr || floatCls == nullptr) {
            return 0;
        }

    //     Get the value of each Float list object in the array
        jsize length = env->GetArrayLength(list);

        //If empty
        if (length < 1) {
            env->DeleteLocalRef(arrayListCls);
            env->DeleteLocalRef(floatCls);
            return 0;
        }

// Creating an output jObjectArray
    jobjectArray outJNIArray = env->NewObjectArray(length, arrayListCls, 0);

        //taking list of X and Y points object at the time of return
        jobject  xPoint,yPoint,xReturnObject,yReturnObject;

            //getting the xList,yList object from the array
            jobject xObjFloatList = env->GetObjectArrayElement(list, 0);
            jobject yObjFloatList = env->GetObjectArrayElement(list, 1);


     // number of elements present in the array object
        int xPointCounts = static_cast<int>(env->CallIntMethod(xObjFloatList, alSizeId));

        static jfloat xReturn, yReturn;
                jobject xReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);
        jobject yReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);

    for (int j = 0; j < xPointCounts; j++) {
            //Getting the x points from the x object list in the array
            xPoint = env->CallObjectMethod(xObjFloatList, alGetId, j);
            //Getting the y points from the y object list in the array
            yPoint = env->CallObjectMethod(yObjFloatList, alGetId, j);

//Returning jobjectArray(Here I am returning the same x and points I am receiving from java side, just to show how to make the returning `jobjectArray`)  

            //float x and y values
            xReturn =static_cast<jfloat >(env->CallFloatMethod(xPoint, floatId,j));
            yReturn =static_cast<jfloat >(env->CallFloatMethod(yPoint, floatId,j));


            xReturnObject = env->NewObject(floatCls,floatConstructor,xReturn);
             yReturnObject = env->NewObject(floatCls,floatConstructor,yReturn);

            env->CallBooleanMethod(xReturnArrayList,addElementToList,xReturnObject);


            env->CallBooleanMethod(yReturnArrayList,addElementToList,yReturnObject);
            env->SetObjectArrayElement(outJNIArray,0,xReturnArrayList);
            env->SetObjectArrayElement(outJNIArray,1,yReturnArrayList);
        __android_log_print(ANDROID_LOG_ERROR, "List of X and Y are saved in the array","%d", 3);

    }

    return outJNIArray;
Paramita
źródło
-6

Prostym rozwiązaniem jest zapisanie danych tablicy w pliku z C, a następnie dostęp do pliku z poziomu Java

Jeyanth
źródło