Search code examples
javacjava-native-interface

Update nested java objects from C


I want to pass a structure pointer from Java to c,where it gets updated with the required values and use those values back at my java code. I searched all over internet but couldn't find any required pages, nor i couldn't get any good examples. Please help me with this. I am new to Java/JNI.

Below is my JNI c file used

typedef struct _struct1{
    char a;
    char b;
    short c;
    char d[20];
    int e;
    int f;
}struct1;

typedef struct _struct2{
    struct1 g[12];
}struct2;

JNIEXPORT void JNICALL Java_com_example_test_GetDataFromC(
        JNIEnv *env,
        jobject /* this */,jobject foo) {
    /* I want to update foo with the values from struct2*/
   test_application(&struct2);
}

Below is the java code

    public class struct1{
        public char a;
        public char b;
        public short c;
        public char []d= new char[20];
        public int e;
        public int f;
    }

    public class struct2{
        public struct1[]g= new struct1[12];
    }

 protected void onCreate(Bundle savedInstanceState) {
    struct2 foo = new struct2();
    GetDataFromC(foo);
    printf("f = %d \n",foo.g[1].f);
 }

 public native void GetDataFromC(struct2);


Solution

  • In a nutshell, you need to do the following:

    // Get foo.g
    jclass cls_struct2 = env->FindClass("struct2");
    jfieldID fld_struct2_g = env->GetFieldID(cls_struct2, "g", "[Lstruct2;");
    jarray foo_g = (jarray) env->GetObjectField(foo, fld_struct2_g);
    
    // Look up field IDs of struct1
    jclass cls_struct1 = env->FindClass("struct1");
    jfieldID fld_struct1_a = env->GetFieldID(cls_struct1, "a", "C");
    jfieldID fld_struct1_d = env->GetFieldID(cls_struct1, "d", "[C");
    
    // Loop over the array
    jsize s = env->GetArrayLength(foo_g);
    for (int i = 0; i < s; i++) {
      jobject element = env->GetObjectArrayElement(foo_g, i);
      env->SetCharField(element, fld_struct1_a, _struct2[i].a);
      jcharArray element_d = (jcharArray) env->GetObjectField(element, fld_struct1_d);
      env->SetCharArrayRegion(element_d, 0, sizeof(_struct2[i].d) / sizeof(_struct2[i].d[0]), _struct2[i].d);
    }
    

    If the char[20] fields are actually null-terminated strings, you may be better off converting them to String and using NewStringUTF on the C++ side.

    EDIT: The answer above is C++ instead of C, but that is mostly cosmetic (env->Method(ARGS) should be (*env)->Method(env, ARGS)

    EDIT 2: From your question it is not clear if your Java code is in a namespace. If it is, you may need pkg/path/to/MyOuterClass$struct1.

    EDIT 3: Corrected modification of .d