WHAT IS JAVA NATIVE INTERFACE?
Java Native Interface (JNI) is a neat thing. It allows running Java code from native applications and libraries written in other languages.
Qt has provided several classes which make using JNI pretty straightforward, amongst which we will single out QAndroidJniEnvironment and QAndroidJniObject class. This article will mostly concentrate on how to use the QAndroidJniObject class which provides APIs to call Java code from C++,which can be very useful if you want to use both Qt and Android classes for Android development. For example – you define a C++ class which instantiates a Java class that registers some Android sensors, gathers data from them and returns it back to the C++ class which can then display that data in a convenient way using QML.
RUNNING JAVA CODE FROM C++
At this point I will assume that you have written the required Java class. To use it from the C++ class, put the .java file in the subfolder path /src/com/mycompanyname/myappname starting from the location of AndroidManifest.xml. Furthermore, add the Android Extras module in the project file:
QT += androidextras
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
DISTFILES += android/AndroidManifest.xml
OTHER_FILES += android/src/com/mycompanyname/myappname/myjavaclass.java
Before I get into details on how to setup the interface, there are a few important points that have to be covered:
- always use fully-qualified class names, such as “java/lang/Class”
- method signatures are structured as (A)R, where A is the argument type and R is the return type*
- all object types are returned as a QAndroidJniObject
Let’s get started then – to initialize a Java class use the following syntax:
In some cases, it is very useful to be able to use the main activity of the application or the application context inside the Java class. To get them, use the appropriate functions from the QtAndroid namespace and send them as arguments to the Java class constructor:
Congratulations, the Java class is now up and running, but what if you want to call a specific function inside that class? To call a function in Java class that takes no arguments and does not return anything, use:
To call some more complex functions, you need to supply the correct function signature*. It is important to note that array types in the signature have the ‘[‘ suffix and fully-qualified types have the ‘L’ prefix and ‘;’ suffix.
For example, to call a function in Java class that takes a string argument and returns an integer, use:
CALLBACK A C++ FUNCTION FROM JAVA
That’s it, you just learned how to run your Java code from C++ using some Qt classes. But wait, let’s turn things over a bit. What if you want to call native C++ code from Java? To do that, create a corresponding function declaration in Java and prefix it with the native keyword. Besides that, you need to map the Java native function to a function in your C++ code. Luckily, there is a useful function called RegisterNatives() which can be used through the QAndroidJniEnvironment object.
First declare an array of JNI native functions that reside in the Java class:
Here, firstNativeFunction and secondNativeFunction represent native functions in Java code:
and cppFunctionOne and cppFunctionTwo are static C++ functions:
Register them in the following way:
Because most objects received from Java are local references, they are only valid in the scope you received them. It is good practice to delete that reference after the registration is complete. And that’s it, you can now run your Java code from C++ and vice versa without too much hassle.
*Here is a list of tables containing specific JNI types from the official Qt documentation:
JNI object types:
JNI primitive types: