Search code examples
javaandroidc++android-ndkjava-native-interface

make native code access java methods and data members


I am working on an android project with native code that is supposed to update a List and some other boolean variables in an object.

consider the following code

some java class in my code looks like:

 class ReturnObject
{
    boolean a, b;
    public List<String[]> listA;

}

public class foo
{
    public native void someFunction(ReturnObject returnObject);
}

and the native code looks like:

JNIEXPORT void JNICALL Java_com_example_androidtest_TestActivity_someFunction
  (JNIEnv * env, jobject jObj, jobject returnObject) {
    std::string f = "foo";
    // missing code here
    // returnObject.add(f) // add like in java
}

How can I set the values of the boolean variables?

and for the the List (knowing my data in the native code are stored as std::string), how can I invoke add(String[] string) method of the List class from native code?


Solution

  • Here is the sample code to set the boolean values, follow the same guideline to call the add method of the list.

    #include <stdio.h>
    #include <jni.h>
    #include "com_example_Foo.h"
    
    JNIEXPORT void JNICALL Java_com_example_Foo_someFunction
    (JNIEnv *env, jobject object, jobject returnObject)
    {
        jclass clzReturnObject = env->FindClass("com/example/ReturnObject");
        jfieldID fieldA = env->GetFieldID(clzReturnObject, "a", "Z");
        jfieldID fieldB = env->GetFieldID(clzReturnObject, "b", "Z");
    
        env->SetBooleanField(returnObject, fieldA, true);
        if (env->ExceptionCheck()) {
            fprintf(stderr, "error set boolean for a");
            env->ExceptionDescribe();
        }
        env->SetBooleanField(returnObject, fieldB, false);
        if (env->ExceptionCheck()) {
            fprintf(stderr, "error set boolean for b");
            env->ExceptionDescribe();
        }
    
        jfieldID fieldListID = env->GetFieldID(clzReturnObject, "list",
                "Ljava/util/List;");
        jobject listObject = env->GetObjectField(returnObject, fieldListID);
    
        jclass clzList = env->FindClass("java/util/List");
        jmethodID addMethodID = env->GetMethodID(clzList, "add",
                "(Ljava/lang/Object;)Z");
    
        jclass clzString = env->FindClass("java/lang/String");
    
        jstring initElement = env->NewStringUTF("0000");
    
        jobjectArray toAdd = env->NewObjectArray(10, clzString, initElement);
    
        if (env->ExceptionCheck()) {
            fprintf(stderr, "error create string array");
            env->ExceptionDescribe();
        }
    
        env->CallBooleanMethod(listObject, addMethodID, toAdd);
        env->DeleteLocalRef(initElement);
    }
    

    Following is the java code

    public class Foo {
    
    static {
        System.loadLibrary("Foo");
    }
    
    public static void main(String[] args) {
    
        ReturnObject returnObject = new ReturnObject();
        returnObject.list = new ArrayList<>();
    
        Foo foo = new Foo();
        foo.someFunction(returnObject);
        System.out.println("size is " + returnObject.list.size());
    
        foo.someFunction(returnObject);
        System.out.println("size is " + returnObject.list.size());
    
        foo.someFunction(returnObject);
        System.out.println("size is " + returnObject.list.size());
    
        // we have three element added from JNI
        for (String string : returnObject.list.get(2)) {
            System.out.println(string);
        }
    }
    
    public native void someFunction(ReturnObject returnObject);
    }