Necessitas: convenient solution for communication between Qt application and Java

转载 2013年10月20日 22:22:29

Necessitas: convenient solution for communication between Qt application and Java

This is my first article about Qt / Necessitas / Android / JNI, so if you will find some error or have some ideas, please, contact me. I didn't use these technologies before. Everything what I present, works with my software configuration: Qt 4.8.0, Necessitas 0.3.4, Android 3.2.

In this article I will present my solution for using Java in Necessitas projects. As far as most of you know, to use Java code in C++ applications and vice-versa, you need to use Java Native Interface (JNI). This technology is used in Necessitas projects and you can find it for example in file qtmain_android.cpp located in:necessitas/Android/Qt/{{VERSION_OF_QT}}/armeabi-v7a/src/android/cpp. In my case, {{VERSION_OF_QT}} is 480. This file is included to your projects during building process.

We can say that qtmain_android.cpp is the most important file for JNI in your project because it containsJNI_OnLoad(.). This function is the only one which is allowed to load any Java class, so this is the only place in your application where you can load these yours. Of course, it's possible to load Java classes in other functions, but you are limited to use only some of these from java.* package.

I found an article (little bit vague for me) which deliver some solution for this problem: How to use Java from Qt/C++ in Necessitas.

There is no code example but fortunately I found this: QT in Android -- Example for accessing the GPS Service.

In both cases, authors are changing qtmain_android.cpp file. This approach has two disadvantages:

  1. qtmain_android.cpp is common for all projects, so all your projects require all classes that you load there,
  2. you can use only one version of constructor of your class,
  3. qtmain_android.cpp is part of Necessitas and it will be probably changed in newer versions.

Let's deal with first two disadvantages. It's hard to create special class which would be good for all possible applications ((-: ) so we need to find another solution. Let's make little changes in qtmain_android.cpp file.

Firstly, add this line before JNI_OnLoad(.) definition (of course outside bodies of functions!):

extern int JavaClassesLoader (JNIEnv* env);

Secondly, put this code into JNI_OnLoad(.) defintion:

    if (!JavaClassesLoader (m_env)){
        __android_log_print (ANDROID_LOG_FATAL, "Qt", "Couldn't register user defined classes!");
        return -1;

Notice, that we use m_env variable so we need to put it below this line:

m_env = uenv.nativeEnvironment;

Thirdly, we need to remove static key-words at this two lines:

static JavaVM *m_javaVM = NULL;
static JNIEnv *m_env = NULL;

And that's all what we need to change in qtmain_android.cpp file.

Now, let's add file which will load our Java classes. In NecessitasQtCreator choose File -> New File or Project -> (Files and Classes) C++ -> C++ Source File -> Choose... Set a Name to JavaClassesLoader.cpp, click Continue and after Done. Fill your file with this code:

#include <jni.h>
#include <android/log.h>

jobject classPointer;

int JavaClassesLoader (JNIEnv* env){
        const char* className = "pl/ekk/mkk/necessitas/YourClassName";
        jclass clazz=env->FindClass(className);
        if (!clazz)
            __android_log_print(ANDROID_LOG_FATAL,"Qt", "Unable to find class '%s'", className);
            return JNI_FALSE;
        jmethodID constr = env->GetMethodID(clazz, "<init>", "()V");
        if(!constr) {
            __android_log_print(ANDROID_LOG_FATAL,"Qt", "Unable to find a constructor for class '%s'", className);
            return JNI_FALSE;
        jobject obj = env->NewObject(clazz, constr);
        classPointer = env->NewGlobalRef(obj);
    return JNI_TRUE;

I guess that you already know that to use multiple Java classes in your project, you just need to create otherjobject classPointer, copy-paste code of JavaClassesLoader(.) function (without last line!) and change value of constant className.

To use different constructor than version without arguments, you need to set different method signatures (see more here). To set arguments values, just add them to NewObject (.) method.

But where should you place your Java file? In your project folder you have android/src/ branch. This is right localisation for your files. Notice, that your classes are inside packages, so you need to create folder trees corresponding to packages that you are using. After you copy your files you need to add them to Necessitas project (Add Existing Files).

Now we can use your Java classes in your project. Open your C++ filein which you want use JNI, put this four lines at its beginning:

#include <jni.h>
#include <qDebug>
extern JavaVM *m_javaVM;
extern jobject classPointer;

This will give us a way to use variables from previous files. To call JNI functions, you need to use this code:

JNIEnv* env;
if (m_javaVM -> AttachCurrentThread (&env, NULL) < 0){
    qCritical() << "AttachCurrentThread failed";

jclass applicationClass = env -> GetObjectClass (classPointer);

if (applicationClass){
    //Communication between C++ and JAVA!

m_javaVM -> DetachCurrentThread ();

That's all! Now you can use your Java classes in more customized way! (-:

I hope that I explained it quite clearly. If your C++ files cannot find variables from different files that we changed, please clean your project (Build -> Clean Project...).

If you still have some problem, I attach small example.

If you want, you can try to use #ifdef#define and #endif in qtmain_android.cpp to easily turn on and turn off usingJavaClassesLoader(.) in your projects.

But... still we have a third disadvantage... I have to admit that I didn't found a good solution for that. Here, I present an effect of my research, I hope that someone will find some good way to deal with it.

It is possible to access to Java Virtual Machine without changing qtmain_android.cpp file (so we don't need to do third step).

The biggest problem is how to use JavaClassesLoader(.) function outside qtmain_android.cpp file, or in another words, outside JNI_OnLoad(.) definition. You can find an explanation of this problem here: FindClass failed.

I tried to use this solutions: Using JNI from a Native Activity, but unfortunately FindClass method can't findjava.lang.ClassLoader, so it seems to be useless for us.

Well, for now, that's all. If anyone will find some solution for third disadvantage or want to discuss about my solution for first one, please contact me.



Preparing for Application and Service Deployment-Differences Between the Storage Emulator and Window The Windows Azure storage emulator acts a...

Handle inter-process communication between PhantomJS and Golang processes via hixie-76 websockets

Although PhantomJS version 1.9 does have WebSocket support, that support is limited to the hixie-76 ...

Communication between TwinCAT and Bus Terminal Controller

Communication between TwinCAT and Bus Terminal Controller   BECKHOFF Fieldbus Compon...

Communication Between Hosting System and Its Plugin Applications(Cross Domains)

The first rendered HTML code in mother system is: if(!window.hasInitShop88Addon){ in...

Example: Communication between Activity and Service using Messaging


solution for communication system

  • 2014年09月09日 00:20
  • 1.39MB
  • 下载

Interaction between C# Application and Oracle through Custom Object

Introduction As a developer, I am very much fond of OOPS and its implementation in C#. When we talk...

A Simple C++ Plugin System -- Interface Between Plugin and Main Application

Abstract前人已有A Simple C++ Plugin System Summary,本文将处理,如何实现插件与主程序之间的接口Introduction支持任意的命令:插件的基本类往往只提供少...
您举报文章:Necessitas: convenient solution for communication between Qt application and Java