JNI Programming

557 Views Asked by At

I'm new to jni programming. I'd like to pass a float array from java to jni, allocate sufficient memory dynamically to float array in jni side, store some values in the jfloatArray, and access it in java. I don't want to return the jfloatArray, just modify the input float array passed. I tried the below method but it is not modifying my java float array. How to achieve this?

Java Code:

    float[] pointList = null;
    outputBitmap = callJNIFunc(pointList, inputBitmap);

JNI Code:

Bitmap callJNIFunc(JNIEnv *env, jfloatArray pointListInPixels, jobject inputBitmap) {
  pointListInPixels = (env)->NewFloatArray(pointListSize.M * 2);
  env->SetFloatArrayRegion(pointListInPixels, 0, pointListSize.M * 2, pointFloats);
}

I read from pass data between Java and C that this can be achieved by passing a Custom Object. However, I'm not quite sure how to do this from jni for a java Object containing float array like this

public class CustomObject{
  public  float[] points;
  public float[] getPoints() {
    return points;
  }

  public void setPoints(float[] points) {
    this.points = points;
  }

}
2

There are 2 best solutions below

4
On

Look at the first line of your JNI function.

Initially, pointListInPixels represents the address of a Java object - the float[] pointList in your java code.

In the very next line, you assign it to (env)->NewFloatArray, which means that pointListInPixels no longer points to your float[] pointList in Java, but to a new array. As a result, your call to SetFloatArrayRegion has no effect. It modifies an array in Java, sure, but it doesn't modify the array you want it to modify.

The correct method of doing this is to make your JNI function return a jFloatArray, converting your java code to:

    float[] pointList = callJNIFunc();

and your JNI code to:

    jFloatArray pointListInPixels = (env)->NewFloatArray(pointListSize.M * 2);
    env->SetFloatArrayRegion(pointListInPixels, 0, pointListSize.M * 2, pointFloats);
    return pointListInPixels;

Method signatures should be changed as required.

0
On

You can create container class for returning value like this:

public class Result{
    public final Bitmap bitmap;
    public final float[] pointListInPixels;

    public Result(Bitmap bitmap, float[] pointListInPixels){
        this.bitmap = bitmap;
        this.pointListInPixels = pointListInPixels;  
    }
}

construct and return Result object from jni:

Result callJNIFunc(JNIEnv *env, jfloatArray pointListInPixels, jobject inputBitmap) {
  // load class by name
  jclass cls = env->FindClass("my/package/name/Result");
  if (env->ExceptionOccurred()) return NULL;

  // take constructor by signature
  jmethodID constructorId = env->GetMethodID(cls, "<init>", "(android/graphics/Bitmap, [[F)V");
  if (env->ExceptionOccurred()) return NULL;

  jobject output_bitmap; // create bitmap and implement logic 

  pointListInPixels = (env)->NewFloatArray(pointListSize.M * 2);
  env->SetFloatArrayRegion(pointListInPixels, 0, pointListSize.M * 2, pointFloats);

  // create result object
  jobject result = env->NewObject(cls, constructorId, output_bitmap, pointListInPixels);

  return result;
}

For this code sample you don't need to delete objects becouse of they are will created by java api (inside of java heap). You can use simple meta loader for java objects from this code snippet