tags:

views:

1166

answers:

2

JNI tutorials, for instance this one, cover quite well how to access primitive fields within an object, as well as how to access arrays that are provided as explicit function arguments (i.e. as subclasses of jarray). But how to access Java (primitive) arrays that are fields within an jobject? For instance, I'd like to operate on the byte array of the following Java object:

class JavaClass {
  ...
  int i;
  byte[] a;
}

The main program could be something like this:

class Test {

  public static void main(String[] args) {
    JavaClass jc = new JavaClass();
    jc.a = new byte[100];
    ...
    process(jc);
  }

  public static native void process(JavaClass jc);
}

The corresponding C++ side would then be:

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {

  jclass jcClass = env->GetObjectClass(jc);
  jfieldID iId = env->GetFieldID(jcClass, "i", "I");

  // This way we can get and set the "i" field. Let's double it:
  jint i = env->GetIntField(jc, iId);
  env->SetIntField(jc, iId, i * 2);

  // The jfieldID of the "a" field (byte array) can be got like this:
  jfieldID aId = env->GetFieldID(jcClass, "a", "[B");

  // But how do we operate on the array???
}

I was thinking to use GetByteArrayElements, but it wants an ArrayType as its argument. Obviously I'm missing something. Is there a way to to this?

+1  A: 

I hope that will help you a little (check out the JNI Struct reference, too):

// Get the class
jclass mvclass = env->GetObjectClass( *cls );
// Get method ID for method getSomeDoubleArray that returns a double array
jmethodID mid = env->GetMethodID( mvclass, "getSomeDoubleArray", "()[D");
// Call the method, returns JObject (because Array is instance of Object)
jobject mvdata = env->CallObjectMethod( *base, mid);
// Cast it to a jdoublearray
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
// Get the elements (you probably have to fetch the length of the array as well
double * data = env->GetDoubleArrayElements(*arr, NULL);
// Don't forget to release it 
env->ReleaseDoubleArrayElements(*arr, data, 0);

Ok here I operate with a method instead of a field (I considered calling a Java getter cleaner) but you probably can rewrite it for the fields as well. Don't forget to release and as in the comment you'll probably still need to get the length.

Edit: Rewrite of your example to get it for a field. Basically replace CallObjectMethod by GetObjectField.

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {

  jclass jcClass = env->GetObjectClass(jc);
  jfieldID iId = env->GetFieldID(jcClass, "i", "I");

  // This way we can get and set the "i" field. Let's double it:
  jint i = env->GetIntField(jc, iId);
  env->SetIntField(jc, iId, i * 2);

  // The jfieldID of the "a" field (byte array) can be got like this:
  jfieldID aId = env->GetFieldID(jcClass, "a", "[B");

  // Get the object field, returns JObject (because Array is instance of Object)
  jobject mvdata = env->GetObjectField (jc, aID);

  // Cast it to a jdoublearray
  jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)

  // Get the elements (you probably have to fetch the length of the array as well  
  double * data = env->GetDoubleArrayElements(*arr, NULL);

  // Don't forget to release it 
  env->ReleaseDoubleArrayElements(*arr, data, 0);
}
Daff
Thanks; it's clever (and maybe even cleaner) to use getters. I'll have to do it this way unless someone points how to directly get the array fields, in GetXXXField-like fashion.
Joonas Pulakka
Ok I added you example for the field (basically just use GetObjectField instead of CallObjectMethod). Though I can of course not guarantee that it'll run out of the box I hope you can get the general idea :)
Daff
Right! Somehow I was expecting to find a bit more straightforward way to do this, and so I was reluctant to go back to definitions ("array is an object" :-) Programming psychology... Thanks again!
Joonas Pulakka
A: 

But when i use the second approach for the class JavaClass { ... int i; char[] a; int b; } it is giving Ljava/lang/ArrayIndexOutOfBoundsException; when i try to call GetIntField for b

srinivas