Write JNI wrappers for Android

Code snippets | Guides | Thoughts By 2 years ago

Following are the steps that you need to follow in order to make things working –

1. Setting up the NDK on my machine
2. Learn how to write JNI wrappers
3. Learn C++ briefly so i can understand the framework
4. Learn how to consume C++ framework in JNI and provide information to Android side of code.

You can’t not explain everything in a single tutorial. In this tutorial, I’ll only be explaining how to write JNI wrappers that get the information from C++ framework and pass it to your Java code. Let’s jump into the code now.

Things I’m going to explain are –

1. Access Java class and create an instance
2. Access and set Java variables
3. Call Java method
4. Access C++ code

1. Access Java class –

JNIEXPORT jobject JNICALL Java_com_b2cloud_example_ClassName_getClass (JNIEnv *env, jobject) {

    //Obtain java class
    jclass jArrayList = env->FindClass("java/util/ArrayList");

    //Obtain the init method
    jmethodID constructor = env->GetMethodID(jArrayList, "", "()V");
    //Create a new object of ArrayList
    jobject jActionArrayList = env->NewObject(jArrayList, constructor);

    return jArrayList;

In above code, jActionArrayList is a java equivalent object. You can call this function from your Java code to get instance of ArrayList.

2. Access and set Java variables

    jclass cls = env->FindClass("com/sample/SampleClass");
    jmethodID constructor = env->GetMethodID(cls, "", "()V");
    jobject obj = env->NewObject(cls, constructor);    
    jfieldID var = env->GetFieldID(cls, "mYourVariableName", "Ljava/lang/String;");
    const char* someChar = "abc";
    env->SetObjectField(obj, var, env->NewStringUTF(someChar));

GetFieldID takes three params – Class name, variable name that you have in your Java class and signature type of the variable which based on the data type of the variable. You can either learn how to write these signatures or simply execute the following command in terminal to generate it –

javah -jni path/to/the/javawrapper

3. Call Java method

env->CallVoidMethod(jobject, jmethodId, params);

In above piece of code, there are few things to notice. First JNI method name is CallVoidMethod, and its different for each return type, For ex. for boolean, its going to be CallBooleanMethod and same goes to other primitive types.

Params explained –

First param should be the jobject of your Java class.
Second param should be the jmethodId of your Java method that you want to call.
Third param should be the params that’s required by your Java method.

4. Access C++ code –

There is no trick here. You just need to include the C++ file to your JNI wrapper class and use it as you use it in your C++ code.

Note – Make sure you keep the reference of your C++ class instance. Keeping C++ object reference is really important as when you send the result back to you Java class from JNI wrapper, there are good chances that you loose track of C++ objects. By keeping the reference of C++ object you can obtain it back when you need it later in the code. In order to achieve it, I keep a variable of long type in my Java class that holds the memory location of the C++ object and when I need to use it later in my code I pass this long value along in JNI method calls.