Você está na página 1de 4

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:

// Instantiate the Java class


QAndroidJniObject m_javaClass = QAndroidJniObject
("com/mycompanyname/myappname/myjavaclassname");
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:

// Instantiate the java class, send current application context and


activity as arguments to the constructor
m_javaClass = QAndroidJniObject
("com/mycompanyname/myappname/myjavaclassname",

"(Landroid/content/Context;Landroid/app/Activity;)V",
QtAndroid::androidContext().object<jobject>(),

QtAndroid::androidActivity().object<jobject>());
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:

m_javaClass.callMethod<void>("javafunctionname");
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:

jint integerVariable = m_javaClass.callMethod<jint>("javafunctionname",


"Ljava/lang/String;)I",
"stringargument");

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:

// An array of JNI native methods in java code


JNINativeMethod methods[] = {
{"firstNativeFunction", "(DDLjava/lang/String;FI)V",
reinterpret_cast<void*>(cppFunctionOne)},
{"secondNativeFunction", "(I)V",
reinterpret_cast<void*>(cppFunctionTwo)}
};
Here, firstNativeFunction and secondNativeFunction represent native functions in Java code:

public native void firstNativeFunction (double a, double b, String c, float


d, int e);

public native void secondNativeFunction(int f);


and cppFunctionOne and cppFunctionTwo are static C++ functions:

static void cppFunctionOne (JNIEnv *env, jobject obj, jdouble a, jdouble


b, jstring c, jfloat d,
jint e) {
qDebug() << "Inside first C++ function";
}

static void cppFunctionTwo (JNIEnv *env, jobject obj, jint f) {


qDebug() << "Inside second C++ function";
}
Register them in the following way:

QAndroidJniEnvironment env;
jclass objectClass = env->GetObjectClass(m_javaClass.object<jobject>());

env->RegisterNatives(objectClass, methods, sizeof (methods) /


sizeof(methods[0]));
env->DeleteLocalRef(objectClass);
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:


Type
jobject
jclass
jstring
jthrowable
jobjectArray
jarray

JNI primitive types:


Type
jboolean
jbyte
jchar
jshort
jint
jlong
jfloat
jdouble
Other types:
Type
void
Custom type

Você também pode gostar