Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Update: Here you have also the Chinese version, thanks goes to Foruok.
After we've seen how to set up the development environment, how to use Qt on Android and what deployment systems are available and how to sign the final package, it's time to move forward. In this article we are going to learn about JNI.
Because it is impossible for Qt to implement all Android features. To use any of them we'll need to use JNI to access them. JNI the only way to do calls to/from Java world from/to native (C/C++) world.
In this article we'll learn the basics of the JNI, in the next article(s) we're going to learn how to use this knowledge to correctly extend Qt on Android apps.
There is tons and tons of information on this matter on the net, in this article I'm focusing only on how to do JNI on Android using Qt.
Starting with 5.2, Qt ships Qt Android Extras module. This module makes our life much more pleasant when we'll have to do JNI.
There are two use cases:
Calling a Java function from C/C++ using Qt Android Extras module is quite easy.
First let's create a Java static method:
// java file android/src/com/kdab/training/MyJavaClass.java
package com.kdab.training;
public class MyJavaClass
{
// this method will be called from C/C++
public static int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
}
So, we defined fibonacci static method into MyJavaClass class into com.kdab.training package. This method returns the computed fibonacci number.
Now let's see how to call it from Qt. The first step is to make sure we are using the android extras, so we need to add it to our .pro file.
# Changes to your .pro file
# ....
QT += androidextras
# ....
Then do the actual call:
// C++ code
#include <QAndroidJniObject>
int fibonacci(int n)
{
return QAndroidJniObject::callStaticMethod<jint>
("com/kdab/training/MyJavaClass" // class name
, "fibonacci" // method name
, "(I)I" // signature
, n);
}
Yes, that's all folks!
Let's take a closer look to this code and see what we have here:
Please check QAndroidJniObject documentation for more information about method signature and the arguments types.
In order to callback a C/C++ function from Java you need to do the follow steps:
Let's change the previous java code a little bit:
// java file android/src/com/kdab/training/MyJavaClass.java
package com.kdab.training;
class MyJavaNatives
{
// declare the native method
public static native void sendFibonaciResult(int n);
}
public class MyJavaClass
{
// this method will be called from C/C++
public static int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
// the second method that will be called from C/C++
public static void compute_fibonacci(int n)
{
// callback the native method with the computed result.
MyJavaNatives.sendFibonaciResult(fibonacci(n));
}
}
Let's take a closer look to this code and see what we have here:
If you try only this code it will fail, because sendFibonaciResult is not registered, and Java VM doesn't know it yet.
Now let's see how to register the function in C/C++.
First thing you need to know is that you can register only function(s). You can NOT register C++ class (non-static) members!
There are two ways to register native methods, we are going to check both ot them:
#include <jni.h>
#include <QDebug>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL
Java_com_kdab_training_MyJavaNatives_sendFibonaciResult(JNIEnv *env,
jobject obj,
jint n)
{
qDebug() << "Computed fibonacci is:" << n;
}
#ifdef __cplusplus
}
#endif
Let's take a closer look to this code and see what we have here:
Using this method to register is quite easy to declare and register, but it has a few disadvantages:
In order to use JNIEnv::RegisterNatives to register native functions, we need to do the following 4 steps to use it:
// C++ code
#include <jni.h>
#include <QDebug>
// define our native method
static void fibonaciResult(JNIEnv */*env*/, jobject /*obj*/, jint n)
{
qDebug() << "Computed fibonacci is:" << n;
}
// step 2
// create a vector with all our JNINativeMethod(s)
static JNINativeMethod methods[] = {
{ "sendFibonaciResult", // const char* function name;
"(I)V", // const char* function signature
(void *)fibonaciResult // function pointer
}
};
// step 1
// this method is called automatically by Java VM
// after the .so file is loaded
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
JNIEnv* env;
// get the JNIEnv pointer.
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)
!= JNI_OK) {
return JNI_ERR;
}
// step 3
// search for Java class which declares the native methods
jclass javaClass = env->FindClass("com/kdab/training/MyJavaNatives");
if (!javaClass)
return JNI_ERR;
// step 4
// register our native methods
if (env->RegisterNatives(javaClass, methods,
sizeof(methods) / sizeof(methods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
Let's check the code for a better understanding:
Even if it looks a little bit complicated to use it, this method has the following advantages:
It's just a matter of taste which method you decide to use to register your native functions.
I do recommend you to use JNIEnv::RegisterNatives as it offers you extra protection because the Java VM checks the functions signature and it throws an exception if it doesn't match.
In this article we've learned the basics of the JNI, in the next article(s) we're going to learn how to use this knowledge to correctly extend Qt on Android apps. We'll talk more about Qt on Android apps architecture, how to extend the Java part of your application and we'll take a real life example to show how to correctly do safe calls from Qt thread to Android UI thread and vice-versa.
Thank you for your time!
Wrapping tiny JS libraries in QML to do quick and simple things effortlessly and elegantly.
Factors to Consider When Choosing a Software Stack
A New Qt (5.x & 6.x) Library for Complete Shared Storage Access
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
19 Comments
25 - Nov - 2014
brexis
Awesome! Thanks
25 - Nov - 2014
Joe Coder
Do you mean, for something as simple as getting the list of contacts it we need to create a Java wrapper first?
This dilutes the multiplatform experience of Qt.
26 - Nov - 2014
BogDan Vatra
You can use JNI from Qt as long as the class don't need to be run on Android UI thread. Otherwise you'll need to write a few lines of Java code.
23 - Dec - 2014
Pablo
BogDan, what do you mean? Cannot I use JNI to send a string from C++ to Java so to be displayed on Android UI?
8 - Jan - 2015
BogDan Vatra
If you'll like to "show" the string from Qt Threat, no, it will not work.
25 - Nov - 2014
d3fault
Very informative, thanks.
10 - Dec - 2014
foruok
Hi,BogDan,here is Chinese version: http://blog.csdn.net/foruok/article/details/41826085
11 - Dec - 2014
foruok
hi,BogDan,I'd written a book about Qt and Qt on Android(in Chinese), and was published recently.You and your articles give me too much help,I'm more grateful for this than words can tell.Here is the link:http://product.china-pub.com/3804179.The book named "Qt on Android核心编程",in this book I talked about a set of basic topics about Qt, and then focus on Qt on Android,include the internal mechanism,common Android topics in Qt.
22 - Jan - 2015
BogDan Vatra
Hi foruok,
Sorry for the slow reply. I just want to say Congrats to you!
5 - Mar - 2015
Nikolaos
Could i find an example how to use QT and libusb for androids with file descriptors? How can i pass the fd to Qt from Java, i think this is very important but i cannot find a useful example!
1 - Apr - 2015
Jyrki Kanerva
Hi! I try to find information on Ministro II compabitility with Android 5 Lollipop. We have a map/navigation software, based on QT and it uses Ministro on installation to download and install needed packages. Its been working great, but now we have a big problem because Ministro is not working with Android 5. Any help or glues? Or info if Ministro will be updated in near future?
1 - Apr - 2015
BogDan Vatra
Sadly, due to an Android bug, it is impossible to use Ministro on Android 5. Try to bundle Qt libs into your package.
1 - Apr - 2015
Jyrki Kanerva
Thanks BogDan! Sad to hear that, but anyhow we now know the problem and will see if we can bundle needed QT libs.
13 - Oct - 2015
Sumit Jadhav
Hi BogDan Vatra, I have one query regarding using JNI in android. Currently i am working on hybrid i.e Qt/QML application. I am calling one static method in java class from c++. In that static method i'm setting new content using m_instance.setContentView(R.layout.main). As i am running this on UI thread, it perfectly displays content from mail.xml. But after completing my task i am not able to unload this view and go back to Qml view. So my question is that, can i use UI loaded in native activity as well as in QML as per my need or am i doing something wrong??
14 - Oct - 2015
BogDan Vatra
Well first and foremost, you should not change the main Activity content view! Instead try to add it as an native view (check http://doc.qt.io/qt-5/qwindow.html#fromWinId ) on top of your QML surface. Then you can set it's visibility and position easily.
24 - Nov - 2015
Sumit Jadhav
Hi BogDan, i read the documentation link you provided but i'm not getting what exactly it trying to say. From where i can get WId. I think with method you mentioned i will get problem mentioned here https://bugreports.qt.io/browse/QTBUG-40159.
It tells to create a empty view.
Can you provide some sample.?
Actually my task is to select multiple images from gallery. So i tried this with different intent parameters like Intent.EXTRA_ALLOW_MULTIPLE and other but some apps allows to select multiple images and some not. I have tried with Intent.ACTION_OPEN_DOCUMENT in introduce in android kitkat but that too has problems with samung devices like samsung galaxy s4. I need to select multiple image in all device. So i'm trying select images with Gridview in java but for that i have to change the activity content. If i change, i will not get QML view back.
4 - Dec - 2015
BogDan Vatra
A fix for QTBUG-40159 is scheduled for 5.6
9 - Dec - 2015
Alexander Koldaev
Hi BogDan! I have a problem. In Java side, I'm subscribe on ACTION_HEADSET_PLUG and receive it successfull. How do I correctly using JNI (may be without using JNI), pass this event into QML-item? Sorry for my English
19 - Jan - 2017
Abraham
Hi BogDan; I wonder how to call Java codes from C++ / Qt without QtAndroidExtras? I want to use jni.h file for this job. Or how does QtAndroidExtras in Android? Thanks.