QJniObject Class▲
-
Header: QJniObject
-
Since: Qt 6.1
-
CMake:
find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
-
qmake: QT += core
Detailed Description▲
The QJniObject class wraps a reference to a Java object, ensuring it isn't gargage-collected and providing access to most JNIEnv method calls (member, static) and fields (setter, getter). It eliminates much boiler-plate that would normally be needed, with direct JNI access, for every operation, including exception-handling.
This API has been designed and tested for use with Android. It has not been tested for other platforms.
General Notes▲
-
Class names need to be fully-qualified, for example: "java/lang/String".
-
Method signatures are written as "(ArgumentsTypes)ReturnType", see JNI Types.
-
All object types are returned as a QJniObject.
Method Signatures▲
QJniObject provides convenience functions that will use the correct signature based on the provided template types. For functions that only return and take JNI types, the signature can be generate at compile time:
jint x =
QJniObject::
callMethod&
lt;jint&
gt;("getSize"
);
QJniObject::
callMethod&
lt;void
&
gt;("touch"
);
jint ret =
jString1.callMethod&
lt;jint&
gt;("compareToIgnoreCase"
, jString2.object&
lt;jstring&
gt;());
These functions are variadic templates, and the compiler will deduce the template arguments from the actual argument types. In many situations, only the return type needs to be provided explicitly.
For functions that take other argument types, you need to supply the signature yourself. It is important that the signature matches the function you want to call. The example below demonstrates how to call different static functions:
// Java class
package org.qtproject.qt;
class
TestClass
{
static
TestClass create() {
... }
static
String fromNumber(int
x) {
... }
static
String[] stringArray(String s1, String s2) {
... }
}
The signature structure is "(ArgumentsTypes)ReturnType". Array types in the signature must have the [ prefix, and the fully-qualified Object type names must have the L prefix and the ; suffix. The signature for the create function is "()Lorg/qtproject/qt/TestClass;. The signatures for the second and third functions are "(I)Ljava/lang/String;" and "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;", respectively.
We can call the create() function like this:
// C++ code
QJniObject testClass =
QJniObject::
callStaticObjectMethod("org/qtproject/qt/TestClass"
,
"create"
,
"()Lorg/qtproject/qt/TestClass;"
);
For the second and third function we can rely on QJniObject's template methods to create the implicit signature string, but we can also pass the signature string explicitly:
// C++ code
QJniObject stringNumber =
QJniObject::
callStaticObjectMethod("org/qtproject/qt/TestClass"
,
"fromNumber"
,
"(I)Ljava/lang/String;"
, 10
);
For the implicit signature creation to work we need to specify the return type explicitly:
// C++ code
QJniObject string1 =
QJniObject::
fromString("String1"
);
QJniObject string2 =
QJniObject::
fromString("String2"
);
QJniObject stringArray =
QJniObject::
callStaticObjectMethod&
lt;jstringArray&
gt;(
"org/qtproject/qt/TestClass"
,
"stringArray"
string1.object&
lt;jstring&
gt;(),
string2.object&
lt;jstring&
gt;());
Note that while he first template parameter specifies the return type of the Java function, the method will still return a QJniObject.
Handling Java Exception▲
After calling Java functions that might throw exceptions, it is important to check for, handle and clear out any exception before continuing. All QJniObject functions handle exceptions internally by reporting and clearing them, saving client code the need to handle exceptions.
The user must handle exceptions manually when doing JNI calls using JNIEnv directly. It is unsafe to make other JNI calls when exceptions are pending. For more information, see QJniEnvironment::checkAndClearExceptions().
Java Native Methods▲
Java native methods makes it possible to call native code from Java, this is done by creating a function declaration in Java and prefixing it with the native keyword. Before a native function can be called from Java, you need to map the Java native function to a native function in your code. Mapping functions can be done by calling QJniEnvironment::registerNativeMethods().
The example below demonstrates how this could be done.
Java implementation:
class
FooJavaClass
{
public
static
void
foo(int
x)
{
if
(x &
lt; 100
)
callNativeOne(x);
else
callNativeTwo(x);
}
private
static
native void
callNativeOne(int
x);
private
static
native void
callNativeTwo(int
x);
}
C++ Implementation:
static
void
fromJavaOne(JNIEnv *
env, jobject thiz, jint x)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
qDebug() &
lt;&
lt; x &
lt;&
lt; "< 100"
;
}
static
void
fromJavaTwo(JNIEnv *
env, jobject thiz, jint x)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
qDebug() &
lt;&
lt; x &
lt;&
lt; ">= 100"
;
}
void
foo()
{
// register the native methods first, ideally it better be done with the app start
const
JNINativeMethod methods[] =
{{
"callNativeOne"
, "(I)V"
, reinterpret_cast
&
lt;void
*&
gt;(fromJavaOne)}
,
{
"callNativeTwo"
, "(I)V"
, reinterpret_cast
&
lt;void
*&
gt;(fromJavaTwo)}}
;
QJniEnvironment env;
env.registerNativeMethods("my/java/project/FooJavaClass"
, methods, 2
);
// Call the java method which will calls back to the C++ functions
QJniObject::
callStaticMethod&
lt;void
&
gt;("my/java/project/FooJavaClass"
, "foo"
, "(I)V"
, 10
); // Output: 10 < 100
QJniObject::
callStaticMethod&
lt;void
&
gt;("my/java/project/FooJavaClass"
, "foo"
, "(I)V"
, 100
); // Output: 100 >= 100
}
The Lifetime of a Java Object▲
Most objects received from Java will be local references and will only stay valid until you return from the native method. After that, the object becomes eligible for garbage collection. If your code creates many local references in a loop you should delete them manually with each iteration, otherwise you might run out of memory. For more information, see JNI Design Overview: Global and Local References. Local references created outside a native method scope must be deleted manually, since the garbage collector will not free them automatically because we are using AttachCurrentThread. For more information, see JNI tips: Local and global references.
If you want to keep a Java object alive you need to either create a new global reference to the object and release it when you are done, or construct a new QJniObject and let it manage the lifetime of the Java object.
The QJniObject only manages its own references, if you construct a QJniObject from a global or local reference that reference will not be released by the QJniObject.
JNI Types▲
Object Types▲
Type |
Signature |
---|---|
jobject |
Ljava/lang/Object; |
jclass |
Ljava/lang/Class; |
jstring |
Ljava/lang/String; |
jthrowable |
Ljava/lang/Throwable; |
jobjectArray |
[Ljava/lang/Object; |
jarray |
[<type> |
jbooleanArray |
[Z |
jbyteArray |
[B |
jcharArray |
[C |
jshortArray |
[S |
jintArray |
[I |
jlongArray |
[J |
jfloatArray |
[F |
jdoubleArray |
[D |
Primitive Types▲
Type |
Signature |
---|---|
jboolean |
Z |
jbyte |
B |
jchar |
C |
jshort |
S |
jint |
I |
jlong |
J |
jfloat |
F |
jdouble |
D |
Other▲
Type |
Signature |
---|---|
void |
V |
Custom type |
L<fully-qualified-name>; |
For more information about JNI, see Java Native Interface Specification.
See Also▲
See also QJniEnvironment, object()
Member Function Documentation▲
[explicit] QJniObject::QJniObject(jclass clazz)▲
Constructs a new JNI object by calling the default constructor of clazz.
The QJniObject will create a new reference to the class clazz and releases it again when it is destroyed. References to the class created outside the QJniObject need to be managed by the caller.
[explicit] QJniObject::QJniObject(jclass clazz, const char *signature, ...)▲
Constructs a new JNI object from clazz by calling the constructor with signature specifying the types of any subsequent arguments.
QJniEnvironment env;
jclass myClazz =
env.findClass("org/qtproject/qt/TestClass"
);
QJniObject(myClazz, "(I)V"
, 3
);
QJniObject::QJniObject(jobject object)▲
Constructs a new JNI object around the Java object object.
The QJniObject will hold a reference to the Java object object and release it when destroyed. Any references to the Java object object outside QJniObject needs to be managed by the caller. In most cases you should never call this function with a local reference unless you intend to manage the local reference yourself. See QJniObject::fromLocalRef() for converting a local reference to a QJniObject.
See Also▲
See also fromLocalRef()