141

I am attempting to use the android NDK.

Is there a way to return an array (in my case an int[]) created in JNI to Java? If so, please provide a quick example of the JNI function that would do this.

-Thanks

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
RyanCheu
  • 3,522
  • 5
  • 38
  • 47

4 Answers4

127

If you've examined the documentation and still have questions that should be part of your initial question. In this case, the JNI function in the example creates a number of arrays. The outer array is comprised of an 'Object' array creating with the JNI function NewObjectArray(). From the perspective of JNI, that's all a two dimensional array is, an object array containing a number of other inner arrays.

The following for loop creates the inner arrays which are of type int[] using the JNI function NewIntArray(). If you just wanted to return a single dimensional array of ints, then the NewIntArray() function is what you'd use to create the return value. If you wanted to create a single dimensional array of Strings then you'd use the NewObjectArray() function but with a different parameter for the class.

Since you want to return an int array, then your code is going to look something like this:

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;
}
Jona
  • 13,325
  • 15
  • 86
  • 129
Jherico
  • 28,584
  • 8
  • 61
  • 87
  • Yeah, I did that already. I was having trouble understanding the example that was related to my problem ( the last one ), and I was wondering if someone would mind explaining a simpler example with just returning an int[]. – RyanCheu Oct 22 '09 at 21:30
  • EDIT: Please ignore my previous comment, the above code does work. Thank you! That was very helpful. – RyanCheu Oct 23 '09 at 21:23
  • 3
    EDIT2: The code works, but you have to change tmp in the SetIntArrayRegion(...) to fill. – RyanCheu Oct 23 '09 at 21:25
  • Don't you get a NPE when you go out of scope - as `fill` is destructed. or does `SetIntArrayRegion` immediately copy the data? – Tobi Akinyemi Apr 17 '21 at 21:54
  • Per the JNI documentation, the SetIntArrayRegion and all the similar primitive array population functions *copy* data into the JNI managed structure. The `fill` array only needs to be valid for the duration of the function. – Jherico Apr 18 '21 at 22:29
  • @Jherico, can you guide about the size variable. I mean the line "result = (*env)->NewIntArray(env, size);", and if we don't know how much size C array will throw back. Can we somehow get the size of incoming array. – Umair Jul 01 '21 at 07:29
  • @Umair In C an array is just a pointer. Any function that takes a pointer to an array of values must also be passed a size value as a separate parameter. There are numerous examples in the standard C library, such as `memset`. – Jherico Apr 07 '22 at 20:54
  • Thanks. I could not understand what is a buffer* in the SetIntArrayRegion method call. Now I see that it is an array. – Farrukh Nabiyev Apr 10 '22 at 21:03
  • `env->SetIntArrayRegion(env, result, 0, size, fill);`, but not `(*env)->SetIntArrayRegion(env, result, 0, size, fill);`. `env` is a pointer. – kgbook Sep 15 '22 at 06:33
43

if someone would like to know how to return String[] array:

java code

private native String[] data();

native export

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

native code

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

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

zajac.m2
  • 1,218
  • 14
  • 13
0

Based on the asked question, this is already explained in the first answer that how can we pass int[] via jobjectArray. But Here is an example how we can return a jobjectArray which contains lists of data. This can be helpful for situations for example: when someone needs to return data in 2D format to draw some line with x and y points. The below example shows how a jobjectArray can return data in the form of following format:

Java input to the JNI:
Array[Arraylist of x float points][Arraylist of y float points]

JNI output to java:
jobjectArray[Arraylist of x float points] [Arraylist of y float points]

    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
  • 83
  • 2
  • 11
-9

Simple solution is that write the array data in a file from C,and then access the file from Java

Jeyanth
  • 5
  • 1