Search code examples
javaandroidcjava-native-interface

How can I pass a Struct from C back to Java via JNI


I have a C struct :

struct MyEntity {

    char *_myEntityType;

};

I create an instance of it :

struct MyEntity my_C_Entity()
{

    struct MyEntity myEntity;

    myEntity . _myEntityType = strdup("user");

    return myEntity;

}

I would like to get struct MyEntity my_C_Entity in Java :

The Java MyEntity Object class

public class MyEntity {

    private String _myEntityType;

    public String get_myEntityType() {
        return _myEntityType;
    }

    public void set_myEntityType(String _myEntityType) {
        _myEntityType = _myEntityType;
    }
}  

Then...

public class FromCPP {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    public native  MyEntity _main();
}  

How can I set MyEntity's Object members from the Struct struct MyEntity in my_C_Entity?

extern "C"
JNIEXPORT jobject JNICALL
Java_myproject_1_FromCPP__1main(JNIEnv *env, jobject instance) {

    // set the values here
}

Solution

  • It can be done something like this:

    jclass ent_clazz = env->FindClass("com/your/package/MyEntity");
    if (!ent_clazz) return NULL;
    
    // Get the IDs of the constructor and the _myEntityType field
    jmethodID ent_init = env->GetMethodID(ent_clazz, "<init>", "()V");
    jfieldID fid = env->GetFieldID(ent_clazz, "_myEntityType", "Ljava/lang/String;");
    if (!ent_init || !fid) return NULL;
    
    // Create a Java MyEntity instance
    jobject result = env->NewObject(ent_clazz, ent_init);
    if (!result || env->ExceptionCheck() != JNI_FALSE) {
        env->ExceptionClear();
        return NULL;
    }
    
    // entity is your C MyEntity instance
    jstring jstr = env->NewStringUTF(entity._myEntityType);
    if (!jstr || env->ExceptionCheck() != JNI_FALSE) {
        env->ExceptionClear();
        return NULL;
    }
    
    // Set the String field
    env->SetObjectField(result, fid, jstr);
    
    return result;
    

    If you added a constructor to your Java MyEntity class that takes an initial value for _myEntityType, you could create the object and initialize the field in one step, which would eliminate the need for GetFieldID and SetObjectField.