javaandroidc++java-native-interfaceandroid-native-library

Passing Custom Java Data Models to my Native Code


I think I have an idea of what to do, I just wanted to poke the SO community to see if I'm on the right track as I haven't seen any information about this specific topic from my research.

I have some custom Java (data model) classes that I want to be accessible on my native C++ side of my application. I realize that these two environments are fairly separate, so I'll have to most likely create a "copy" of my data models in c++, and initialize them in my nativeLib JNI file?

I'm assuming I can't just "convert" the java class to the c++ class, since the compiler probably won't be able to do such a conversion. I'll have to manage each of the fields that I need manually through a JNI method call.

Is this correct, or is there a better way that I'm not aware of?

I suppose an example might look like this:

Foo.java

public class Foo {
    boolean A;
    boolean B;
    boolean C;
}

native-lib.cpp

extern "C" {

    std::unique_ptr<Foo> foo;

    JNIEXPORT void JNICALL
    Java_com_example_user_project_native_1Foo(JNIEnv *env, jobject instance, jbool jbooleanA, jbool jbooleanB, jbool jbooleanC) {

    Foo->setA(jbooleanA);
    Foo->setB(jbooleanB);
    Foo->setC(jbooleanC);
}

Foo.h

class Foo {
private:
    bool A;
    bool B;
    bool C;

public:
    // add setters & getters here, or any other needed functions
}

Solution

  • While finding the solution to my question, I found out more specifics to why the conversion doesn't work:

    1. ArrayList (from Java) is not perceived as a jobjectArray in the JNI framework. It is recognized as a jObject. So if you wanted to convert an ArrayList object you would have to create a new jobjectArray in JNI

    2. You cannot access methods from the ArrayList class (like get, or set), as the class of the parameters is a generic. Generics aren't allowed in JNI

    Because of these two reasons, an ArrayList object cannot be converted and sent through JNI. Instead I chose to convert my ArrayList into an object array myClass[] on the Java side, then send that through.

    Bellow is the code I wrote for this conversion:

    javaClass.java

    void setFooArray(ArrayList<Foo> param) {
        Foo[] fooArray = new Foo[param.size()];
    
        for (int i = 0; i < param.size(); i++) {
            fooArray[i] = param.get(i);
        }
    
        native_setFooArray(fooArray, fooArray.length);
    }
    
    // ...
    
    private native void native_setFooArray(Foo[] fooArray, int size);
    

    native-lib.cpp

    JNIEXPORT void JNICALL
    java_com_example_my_project_native_1setFooArray(JNIEnv *env, jobject instance, jobjectArray fooArray, jint arraySize) {
    
        std::vector<Foocpp> cfooArray;
    
        jfieldID fidA;
        jfieldID fidB;
    
        for (jint i = 0; i < arraySize; i++) {
            jobject jfoo = env->GetObjectArrayElement(fooArray, i);
            jclass cfoo = env->GetObjectClass(jfoo);
    
            fidA = env->getFieldID(cfoo, "A", "I");
            fidB = env->GetFieldID(cfoo, "B", "I");
            jint A = env->GetIntField(cfoo, fidA);
            jint B = env->GetIntField(cfoo, fidB);
    
            const Foocpp foo = Foocpp(A, B);
            cfooArray.push_back(foo);
        }
    
        class->setFooArray(cfooArray);
    

    Useful links: