JNI Callback Tutorial For Android



Watch the Full Tutorial on YouTube: Link to the Video




Introduction

           

Java Native Interface (JNI) is a crucial aspect of Android development that facilitates communication between Java and C/C++ code. JNI enables developers to harness the power of native code while still utilizing the rich features of the Java programming language. In this tutorial, we will walk you through the process of creating a callback mechanism using JNI in an Android application. This callback will allow seamless interaction between Java and C++ components.


Step 1: Create an Android Application


The first step is to set up an Android application project in Android Studio. Choose the native C++ template when creating the project. Upon project creation, you will notice the addition of a 'cpp' folder within your project structure. This folder holds essential files for integrating C++ code into your Android application.

Your CMakeLists.txt file, which is located in the 'cpp' folder, contains configuration details for building the C++ code alongside the Java code.


Step 2: Create the Interface


In order to establish a callback mechanism, we need to define an Interface class. This class will include a function that acts as the callback method from the JNI layer. This function will be invoked in response to certain events.

interface JniCallBack {
fun callbackwithValue(data : Int)
}


Step 3: Implement the Native Function


Within the native-lib.cpp file, located in the 'cpp' folder, you will implement the native function addListener() that will handle the callback. This function will store a reference to the listener object in a global jClass variable, let's call it store_listener.

extern "C"
JNIEXPORT jobject JNICALL
Java_org_droidtv_jnicallbackexample_MainActivity_addListener(JNIEnv *env, jobject/* this*/,
jobject jnicallback) {
env->GetJavaVM(&jvm);
Genv = env;
store_Listener = static_cast<jclass>(Genv->NewGlobalRef(jnicallback));
return nullptr;

}


Step 4: Call the Native Function


Before making any JNI calls, we need to load the native library containing your C++ code. This is usually done in the static block of the MainActivity

companion object {
// Used to load the 'jnicallbackexample' library on application startup.
init {
System.loadLibrary("jnicallbackexample")
}
}

To bridge the gap between Java and C++, you need to define native methods within the MainActivity. These methods will be declared with the native keyword and will act as entry points to your C++ functions.

external fun changeValue(value : Int) : Void
external fun addListener(jnicallback: JniCallBack) : Void


Now that we have loaded the JNI library and defined the native method, we can call the native function from the MainActivity, first we will call addListener() function to pass our interface object.

addListener(this)

Then we will create a function changeValue in the native-lib.cpp file. This function will be triggered when a button is clicked or when a specific event occurs within your application.


Step 5: Implement Callback Mechanism


Inside the function defined in the native-lib.cpp file, changeValue(), we will retrieve the interface class details from the store_listener object. After obtaining the necessary information, we can then invoke the interface method callBackWithValue() while passing the value obtained from the changeValue() function.

extern "C"
JNIEXPORT jobject JNICALL
Java_org_droidtv_jnicallbackexample_MainActivity_changeValue(JNIEnv *env, jobject thiz,
jint value) {
if(store_Listener != nullptr){
jclass clazz = Genv->GetObjectClass(store_Listener);
jmethodID store_method = Genv->GetMethodID(clazz,"callbackwithValue","(I)V");
//Here we have taken the method id with the name and parameter.
Genv->CallVoidMethod(store_Listener,store_method,value);
//And we are calling the java interface function from the jni layer.
}
return nullptr;
}


Step 6: Implement Callback Handling


Finally, back in the MainActivity, we will implement the interface thAT created earlier. This implementation will allow us to receive the updated value from the C++ side and update the user interface accordingly. Through this JNI callback mechanism, we can seamlessly communicate between the Java and C++ components of your application.


override fun callbackwithValue(data: Int) {
binding.sampleText.text = data.toString()
}


Conclusion


Utilizing the Java Native Interface (JNI) for callback functionality in Android applications enables developers to combine the strengths of both Java and C++ languages. This tutorial has provided you with a step-by-step guide on creating a JNI callback mechanism, allowing for smooth communication between your app's components. By mastering JNI and its callback capabilities, you can unlock a new level of versatility and performance in your Android projects.


Post a Comment

Previous Post Next Post