summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjniobject.cpp
diff options
context:
space:
mode:
authorAssam Boudjelthia <assam.boudjelthia@qt.io>2021-01-14 12:38:04 +0200
committerAssam Boudjelthia <assam.boudjelthia@qt.io>2021-01-27 17:23:04 +0200
commit4e60681c879a54cf5b34862a30e27c492ed36363 (patch)
tree01c7d46ae34a6596fa235eea915846b66e7e8670 /src/corelib/kernel/qjniobject.cpp
parent407ce5c0afd905b38a7d82980a76ed834fc971eb (diff)
Make QJniObject and QJniEnvironment public API
As part of Qt 6 restructring for the extras modules, this change exposes the Jni APIs which are very important for Android platform. This patch adds the APIs QJniObject, QJniEnvironment, QJniExceptionCleaner based from private QtCore and QtAndroidExtras. The Jni interface is cross-platform which justifies the name, but currently, this API is used mainly for Android, and the naming comes generic without Android keyword to avoid any future limitation on supporting other platforms. [ChangeLog][QtCore] Add new QJniObject, QJniEnvironment and QJniExceptionCleaner APIs. Task-number: QTBUG-89482 Fixes: QTBUG-89633 Change-Id: I4382dd53a225375759b9d042f6035a4a9810572b Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
Diffstat (limited to 'src/corelib/kernel/qjniobject.cpp')
-rw-r--r--src/corelib/kernel/qjniobject.cpp1872
1 files changed, 1872 insertions, 0 deletions
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
new file mode 100644
index 00000000000..ba7d0af7789
--- /dev/null
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -0,0 +1,1872 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qjnienvironment.h"
+#include "qjnihelpers_p.h"
+#include "qjniobject.h"
+
+#include <QtCore/QReadWriteLock>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QJniObject
+ \inmodule QtCore
+ \brief Provides a convenient set of APIs to call Java code from C++ using the Java Native Interface (JNI).
+ \since 6.1
+
+ \sa QJniEnvironment
+
+ \section1 General Notes
+
+ \list
+ \li Class names needs to contain the fully-qualified class name, for example: \b"java/lang/String".
+ \li Method signatures are written as \b"(Arguments)ReturnType"
+ \li All object types are returned as a QJniObject.
+ \endlist
+
+ \note This API has been tested and meant to be mainly used for Android and it hasn't been tested
+ for other platforms.
+
+ \section1 Method Signatures
+
+ For functions that take no arguments, QJniObject provides convenience functions that will use
+ the correct signature based on the provided template type. For example:
+
+ \code
+ jint x = QJniObject::callMethod<jint>("getSize");
+ QJniObject::callMethod<void>("touch");
+ \endcode
+
+ In other cases you will need to supply the signature yourself, and it is important that the
+ signature matches the function you want to call. The signature structure is \b \(A\)R, where \b A
+ is the type of the argument\(s\) and \b R is the return type. Array types in the signature must
+ have the \b\[ suffix and the fully-qualified type names must have the \b L prefix and \b ; suffix.
+
+ The example below demonstrates how to call two different static functions.
+ \code
+ // Java class
+ package org.qtproject.qt;
+ class TestClass
+ {
+ static String fromNumber(int x) { ... }
+ static String[] stringArray(String s1, String s2) { ... }
+ }
+ \endcode
+
+ The signature for the first function is \b"\(I\)Ljava/lang/String;"
+
+ \code
+ // C++ code
+ QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ "fromNumber"
+ "(I)Ljava/lang/String;",
+ 10);
+ \endcode
+
+ and the signature for the second function is \b"\(Ljava/lang/String;Ljava/lang/String;\)\[Ljava/lang/String;"
+
+ \code
+ // C++ code
+ QJniObject string1 = QJniObject::fromString("String1");
+ QJniObject string2 = QJniObject::fromString("String2");
+ QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ "stringArray"
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
+ string1.object<jstring>(),
+ string2.object<jstring>());
+ \endcode
+
+
+ \section1 Handling Java Exception
+
+ When calling Java functions that might throw an exception, it is important that you check, handle
+ and clear out the exception before continuing.
+
+ \note It is unsafe to make a JNI call when there are exceptions pending. For more information,
+ see QJniEnvironment::exceptionCheckAndClear().
+
+ \section1 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 \b 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 the RegisterNatives() function
+ through the \l{QJniEnvironment}{JNI environment pointer}.
+
+ The example below demonstrates how this could be done.
+
+ Java implementation:
+ \snippet jni/src_qjniobject.cpp Java native methods
+
+ C++ Implementation:
+ \snippet jni/src_qjniobject.cpp Registering native methods
+
+ \section1 The Lifetime of a Java Object
+
+ Most \l{Object types}{objects} received from Java will be local references and will only stay valid
+ in the scope you received them. After that, the object becomes eligible for garbage collection. 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.
+ \sa object()
+
+ \note 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.
+
+ \section1 JNI Types
+
+ \section2 Object Types
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li jobject
+ \li Ljava/lang/Object;
+ \row
+ \li jclass
+ \li Ljava/lang/Class;
+ \row
+ \li jstring
+ \li Ljava/lang/String;
+ \row
+ \li jthrowable
+ \li Ljava/lang/Throwable;
+ \row
+ \li jobjectArray
+ \li [Ljava/lang/Object;
+ \row
+ \li jarray
+ \li [\e<type>
+ \row
+ \li jbooleanArray
+ \li [Z
+ \row
+ \li jbyteArray
+ \li [B
+ \row
+ \li jcharArray
+ \li [C
+ \row
+ \li jshortArray
+ \li [S
+ \row
+ \li jintArray
+ \li [I
+ \row
+ \li jlongArray
+ \li [J
+ \row
+ \li jfloatArray
+ \li [F
+ \row
+ \li jdoubleArray
+ \li [D
+ \endtable
+
+ \section2 Primitive Types
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li jboolean
+ \li Z
+ \row
+ \li jbyte
+ \li B
+ \row
+ \li jchar
+ \li C
+ \row
+ \li jshort
+ \li S
+ \row
+ \li jint
+ \li I
+ \row
+ \li jlong
+ \li J
+ \row
+ \li jfloat
+ \li F
+ \row
+ \li jdouble
+ \li D
+ \endtable
+
+ \section2 Other
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li void
+ \li V
+ \row
+ \li \e{Custom type}
+ \li L\e<fully-qualified-name>;
+ \endtable
+
+ For more information about JNI see: \l http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
+*/
+
+/*!
+ \fn bool operator==(const QJniObject &o1, const QJniObject &o2)
+
+ \relates QJniObject
+
+ Returns true if both objects, \a o1 and \a o2, are referencing the same Java object, or if both
+ are NULL. In any other cases false will be returned.
+*/
+
+/*!
+ \fn bool operator!=(const QJniObject &o1, const QJniObject &o2)
+ \relates QJniObject
+
+ Returns true if \a o1 holds a reference to a different object than \a o2.
+*/
+
+static inline QLatin1String keyBase()
+{
+ return QLatin1String("%1%2:%3");
+}
+
+static QString qt_convertJString(jstring string)
+{
+ QJniEnvironment env;
+ int strLength = env->GetStringLength(string);
+ QString res(strLength, Qt::Uninitialized);
+ env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data()));
+ return res;
+}
+
+typedef QHash<QString, jclass> JClassHash;
+Q_GLOBAL_STATIC(JClassHash, cachedClasses)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
+
+static QByteArray toBinaryEncClassName(const QByteArray &className)
+{
+ return QByteArray(className).replace('/', '.');
+}
+
+static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr)
+{
+ QReadLocker locker(cachedClassesLock);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(QString::fromLatin1(classBinEnc));
+ const bool found = (it != cachedClasses->constEnd());
+
+ if (isCached)
+ *isCached = found;
+
+ return found ? it.value() : 0;
+}
+
+inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
+{
+ const QByteArray &binEncClassName = binEncoded ? className : toBinaryEncClassName(className);
+
+ bool isCached = false;
+ jclass clazz = getCachedClass(binEncClassName, &isCached);
+ if (clazz || isCached)
+ return clazz;
+
+ QJniObject classLoader(QtAndroidPrivate::classLoader());
+ if (!classLoader.isValid())
+ return nullptr;
+
+ QWriteLocker locker(cachedClassesLock);
+ // did we lose the race?
+ const QLatin1String key(binEncClassName);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
+ if (it != cachedClasses->constEnd())
+ return it.value();
+
+ QJniObject stringName = QJniObject::fromString(key);
+ QJniObject classObject = classLoader.callObjectMethod("loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;",
+ stringName.object());
+
+ if (!QJniEnvironment::exceptionCheckAndClear(env) && classObject.isValid())
+ clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
+
+ cachedClasses->insert(key, clazz);
+ return clazz;
+}
+
+typedef QHash<QString, jmethodID> JMethodIDHash;
+Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
+
+static inline jmethodID getMethodID(JNIEnv *env,
+ jclass clazz,
+ const char *name,
+ const char *sig,
+ bool isStatic = false)
+{
+ jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, sig)
+ : env->GetMethodID(clazz, name, sig);
+
+ if (QJniEnvironment::exceptionCheckAndClear(env))
+ return nullptr;
+
+ return id;
+}
+
+static jmethodID getCachedMethodID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *sig,
+ bool isStatic = false)
+{
+ if (className.isEmpty())
+ return getMethodID(env, clazz, name, sig, isStatic);
+
+ const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig));
+ QHash<QString, jmethodID>::const_iterator it;
+
+ {
+ QReadLocker locker(cachedMethodIDLock);
+ it = cachedMethodID->constFind(key);
+ if (it != cachedMethodID->constEnd())
+ return it.value();
+ }
+
+ {
+ QWriteLocker locker(cachedMethodIDLock);
+ it = cachedMethodID->constFind(key);
+ if (it != cachedMethodID->constEnd())
+ return it.value();
+
+ jmethodID id = getMethodID(env, clazz, name, sig, isStatic);
+
+ cachedMethodID->insert(key, id);
+ return id;
+ }
+}
+
+typedef QHash<QString, jfieldID> JFieldIDHash;
+Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
+
+static inline jfieldID getFieldID(JNIEnv *env,
+ jclass clazz,
+ const char *name,
+ const char *sig,
+ bool isStatic = false)
+{
+ jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, sig)
+ : env->GetFieldID(clazz, name, sig);
+
+ if (QJniEnvironment::exceptionCheckAndClear(env))
+ return nullptr;
+
+ return id;
+}
+
+static jfieldID getCachedFieldID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *sig,
+ bool isStatic = false)
+{
+ if (className.isNull())
+ return getFieldID(env, clazz, name, sig, isStatic);
+
+ const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig));
+ QHash<QString, jfieldID>::const_iterator it;
+
+ {
+ QReadLocker locker(cachedFieldIDLock);
+ it = cachedFieldID->constFind(key);
+ if (it != cachedFieldID->constEnd())
+ return it.value();
+ }
+
+ {
+ QWriteLocker locker(cachedFieldIDLock);
+ it = cachedFieldID->constFind(key);
+ if (it != cachedFieldID->constEnd())
+ return it.value();
+
+ jfieldID id = getFieldID(env, clazz, name, sig, isStatic);
+
+ cachedFieldID->insert(key, id);
+ return id;
+ }
+}
+
+jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
+{
+ const QByteArray &classDotEnc = toBinaryEncClassName(className);
+ bool isCached = false;
+ jclass clazz = getCachedClass(classDotEnc, &isCached);
+
+ const bool found = clazz || (!clazz && isCached);
+
+ if (found)
+ return clazz;
+
+ const QLatin1String key(classDotEnc);
+ if (env) { // We got an env. pointer (We expect this to be the right env. and call FindClass())
+ QWriteLocker locker(cachedClassesLock);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
+ // Did we lose the race?
+ if (it != cachedClasses->constEnd())
+ return it.value();
+
+ jclass fclazz = env->FindClass(className);
+ if (!QJniEnvironment::exceptionCheckAndClear(env)) {
+ clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
+ env->DeleteLocalRef(fclazz);
+ }
+
+ if (clazz)
+ cachedClasses->insert(key, clazz);
+ }
+
+ if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
+ clazz = loadClass(classDotEnc, QJniEnvironment(), true);
+
+ return clazz;
+}
+
+class QJniObjectPrivate
+{
+public:
+ QJniObjectPrivate() = default;
+ ~QJniObjectPrivate() {
+ QJniEnvironment env;
+ if (m_jobject)
+ env->DeleteGlobalRef(m_jobject);
+ if (m_jclass && m_own_jclass)
+ env->DeleteGlobalRef(m_jclass);
+ }
+
+ jobject m_jobject = nullptr;
+ jclass m_jclass = nullptr;
+ bool m_own_jclass = true;
+ QByteArray m_className;
+};
+
+/*!
+ \fn QJniObject::QJniObject()
+
+ Constructs an invalid QJniObject.
+
+ \sa isValid()
+*/
+QJniObject::QJniObject()
+ : d(new QJniObjectPrivate())
+{
+}
+
+/*!
+ \fn QJniObject::QJniObject(const char *className)
+
+ Constructs a new QJniObject by calling the default constructor of \a className.
+
+ \code
+ QJniObject myJavaString("java/lang/String");
+ \endcode
+*/
+QJniObject::QJniObject(const char *className)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ // get default constructor
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", "()V");
+ if (constructorId) {
+ jobject obj = env->NewObject(d->m_jclass, constructorId);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(const char *className, const char *signature, ...)
+
+ Constructs a new QJniObject by calling the constructor of \a className with \a signature
+ and arguments.
+
+ \code
+ QJniEnvironment env;
+ char* str = "Hello";
+ jstring myJStringArg = env->NewStringUTF(str);
+ QJniObject myNewJavaString("java/lang/String", "(Ljava/lang/String;)V", myJStringArg);
+ \endcode
+*/
+QJniObject::QJniObject(const char *className, const char *sig, ...)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", sig);
+ if (constructorId) {
+ va_list args;
+ va_start(args, sig);
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ va_end(args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+QJniObject::QJniObject(const char *className, const char *sig, const QVaListPrivate &args)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", sig);
+ if (constructorId) {
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jclass clazz, const char *signature, ...)
+
+ Constructs a new QJniObject from \a clazz by calling the constructor with \a signature
+ and arguments.
+
+ \code
+ QJniEnvironment env;
+ jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
+ QJniObject(myClazz, "(I)V", 3);
+ \endcode
+*/
+QJniObject::QJniObject(jclass clazz, const char *sig, ...)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ if (clazz) {
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", sig);
+ if (constructorId) {
+ va_list args;
+ va_start(args, sig);
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ va_end(args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jclass clazz)
+
+ Constructs a new QJniObject by calling the default constructor of \a clazz.
+
+ Note: The QJniObject will create a new reference to the class \a clazz
+ and releases it again when it is destroyed. References to the class created
+ outside the QJniObject need to be managed by the caller.
+*/
+
+QJniObject::QJniObject(jclass clazz)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ // get default constructor
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", "()V");
+ if (constructorId) {
+ jobject obj = env->NewObject(d->m_jclass, constructorId);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+QJniObject::QJniObject(jclass clazz, const char *sig, const QVaListPrivate &args)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ if (clazz) {
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", sig);
+ if (constructorId) {
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jobject object)
+
+ Constructs a new QJniObject around the Java object \a object.
+
+ \note The QJniObject will hold a reference to the Java object \a object
+ and release it when destroyed. Any references to the Java object \a 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.
+
+ \sa fromLocalRef()
+*/
+QJniObject::QJniObject(jobject obj)
+ : d(new QJniObjectPrivate())
+{
+ if (!obj)
+ return;
+
+ QJniEnvironment env;
+ d->m_jobject = env->NewGlobalRef(obj);
+ jclass cls = env->GetObjectClass(obj);
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
+ env->DeleteLocalRef(cls);
+}
+
+/*!
+ \fn QJniObject::~QJniObject()
+
+ Destroys the QJniObject and releases any references held by the QJniObject.
+*/
+QJniObject::~QJniObject()
+{}
+
+/*!
+ \fn template <typename T> T QJniObject::object() const
+
+ Returns the object held by the QJniObject as type T.
+
+ \code
+ QJniObject string = QJniObject::fromString("Hello, JNI");
+ jstring jstring = string.object<jstring>();
+ \endcode
+
+ \note The returned object is still owned by the QJniObject. If you want to keep the object valid
+ you should create a new QJniObject or make a new global reference to the object and
+ free it yourself.
+
+ \snippet jni/src_qjniobject.cpp QJniObject scope
+
+ \code
+ jobject object = jniObject.object();
+ \endcode
+*/
+Q_CORE_EXPORT jobject QJniObject::object() const
+{
+ return javaObject();
+}
+
+QJniObject QJniObject::callObjectMethodV(const char *methodName,
+ const char *sig,
+ va_list args) const
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);
+ if (id) {
+ res = env->CallObjectMethodV(d->m_jobject, id, args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+QJniObject QJniObject::callStaticObjectMethodV(const char *className,
+ const char *methodName,
+ const char *sig,
+ va_list args)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, sig, true);
+ if (id) {
+ res = env->CallStaticObjectMethodV(clazz, id, args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ va_list args)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true);
+ if (id) {
+ res = env->CallStaticObjectMethodV(clazz, id, args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callMethod(const char *methodName, const char *sig, ...) const
+
+ Calls the method \a methodName with a signature \a sig and returns the value.
+
+ \code
+ QJniObject myJavaString = ...;
+ jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051);
+ \endcode
+
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const char *sig, ...) const
+{
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ env->CallVoidMethodV(d->m_jobject, id, args);
+ va_end(args);
+ env.exceptionCheckAndClear();
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callMethod(const char *methodName) const
+
+ Calls the method \a methodName and returns the value.
+
+ \code
+ QJniObject myJavaString = ...;
+ jint size = myJavaString.callMethod<jint>("length");
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName) const
+{
+ callMethod<void>(methodName, "()V");
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...)
+
+ Calls the static method with \a methodName with \a signature on class \a className with optional arguments.
+
+ \code
+ ...
+ jint a = 2;
+ jint b = 4;
+ jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
+ ...
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className,
+ const char *methodName,
+ const char *sig,
+ ...)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ methodName, sig, true);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.exceptionCheckAndClear();
+ }
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName)
+
+ Calls the static method \a methodName on class \a className and returns the value.
+
+ \code
+ jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, const char *methodName)
+{
+ callStaticMethod<void>(className, methodName, "()V");
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
+
+ Calls the static method \a methodName with \a signature on \a clazz and returns the value.
+
+ \code
+ ...
+ jclass javaMathClass = ...; // ("java/lang/Math")
+ jint a = 2;
+ jint b = 4;
+ jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b);
+ ...
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ ...)
+{
+ QJniEnvironment env;
+ if (clazz) {
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.exceptionCheckAndClear();
+ }
+ }
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(const char *className,
+ const char *methodName,
+ const char *sig,
+ va_list args)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz,
+ toBinaryEncClassName(className), methodName,
+ sig, true);
+ if (id) {
+ env->CallStaticVoidMethodV(clazz, id, args);
+ env.exceptionCheckAndClear();
+ }
+ }
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ va_list args)
+{
+ QJniEnvironment env;
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true);
+ if (id) {
+ env->CallStaticVoidMethodV(clazz, id, args);
+ env.exceptionCheckAndClear();
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName)
+
+ Calls the static method \a methodName on \a clazz and returns the value.
+
+ \code
+ ...
+ jclass javaMathClass = ...; // ("java/lang/Math")
+ jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
+ ...
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz, const char *methodName)
+{
+ callStaticMethod<void>(clazz, methodName, "()V");
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callMethodV<void>(const char *methodName, const char *sig,
+ va_list args) const
+{
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);
+ if (id) {
+ env->CallVoidMethodV(d->m_jobject, id, args);
+ env.exceptionCheckAndClear();
+ }
+}
+
+#define MAKE_JNI_METHODS(MethodName, Type, Signature) \
+template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName, \
+ const char *sig, ...) const \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); \
+ if (id) { \
+ va_list args; \
+ va_start(args, sig); \
+ res = env->Call##MethodName##MethodV(d->m_jobject, id, args); \
+ va_end(args); \
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ } \
+ return res; \
+}\
+template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName) const \
+{ \
+ return callMethod<Type>(methodName, Signature); \
+} \
+\
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
+ const char *methodName, \
+ const char *sig, \
+ ...) \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jclass clazz = loadClass(className, env); \
+ if (clazz) { \
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, \
+ sig, true); \
+ if (id) { \
+ va_list args; \
+ va_start(args, sig); \
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
+ va_end(args); \
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ } \
+ } \
+ return res; \
+} \
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
+ const char *methodName) \
+{ \
+ return callStaticMethod<Type>(className, methodName, Signature); \
+}\
+\
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
+ const char *methodName, \
+ const char *sig, \
+ ...) \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ if (clazz) { \
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true); \
+ if (id) { \
+ va_list args; \
+ va_start(args, sig); \
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
+ va_end(args); \
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ } \
+ } \
+ return res; \
+} \
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
+ const char *methodName) \
+{ \
+ return callStaticMethod<Type>(clazz, methodName, Signature); \
+}\
+template <> \
+Q_CORE_EXPORT Type QJniObject::callMethodV<Type>(const char *methodName, const char *sig,\
+ va_list args) const\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);\
+ if (id) {\
+ res = env->Call##MethodName##MethodV(d->m_jobject, id, args);\
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ }\
+ return res;\
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(const char *className,\
+ const char *methodName,\
+ const char *sig,\
+ va_list args)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jclass clazz = loadClass(className, env);\
+ if (clazz) {\
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,\
+ sig, true);\
+ if (id) {\
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ }\
+ }\
+ return res;\
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(jclass clazz,\
+ const char *methodName,\
+ const char *sig,\
+ va_list args)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true);\
+ if (id) {\
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ }\
+ return res;\
+}
+
+#define DECLARE_JNI_METHODS(MethodName, Type, Signature) MAKE_JNI_METHODS(MethodName, \
+ Type, \
+ Signature)
+DECLARE_JNI_METHODS(Boolean, jboolean, "()Z")
+DECLARE_JNI_METHODS(Byte, jbyte, "()B")
+DECLARE_JNI_METHODS(Char, jchar, "()C")
+DECLARE_JNI_METHODS(Short, jshort, "()S")
+DECLARE_JNI_METHODS(Int, jint, "()I")
+DECLARE_JNI_METHODS(Long, jlong, "()J")
+DECLARE_JNI_METHODS(Float, jfloat, "()F")
+DECLARE_JNI_METHODS(Double, jdouble, "()D")
+
+/*!
+ \fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
+
+ Calls the Java object's method \a methodName with the signature \a signature and arguments
+
+ \code
+ QJniObject myJavaString; ==> "Hello, Java"
+ QJniObject mySubstring = myJavaString.callObjectMethod("substring", "(II)Ljava/lang/String;", 7, 10);
+ \endcode
+*/
+QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sig, ...) const
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ res = env->CallObjectMethodV(d->m_jobject, id, args);
+ va_end(args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...)
+
+ Calls the static method with \a methodName and \a signature on the class \a className.
+
+ \code
+ QJniObject thread = QJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread", "()Ljava/lang/Thread;");
+ QJniObject string = QJniObject::callStaticObjectMethod("java/lang/String", "valueOf", "(I)Ljava/lang/String;", 10);
+ \endcode
+*/
+QJniObject QJniObject::callStaticObjectMethod(const char *className,
+ const char *methodName,
+ const char *sig,
+ ...)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,
+ sig, true);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ res = env->CallStaticObjectMethodV(clazz, id, args);
+ va_end(args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...)
+
+ Calls the static method with \a methodName and \a signature on class \a clazz.
+*/
+QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ ...)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ if (clazz) {
+ jmethodID id = getMethodID(env, clazz, methodName, sig, true);
+ if (id) {
+ va_list args;
+ va_start(args, sig);
+ res = env->CallStaticObjectMethodV(clazz, id, args);
+ va_end(args);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+ }
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QJniObject QJniObject::callObjectMethod(const char *methodName) const
+
+ Calls the Java objects method \a methodName and returns a new QJniObject for
+ the returned Java object.
+
+ \code
+ ...
+ QJniObject myJavaString1 = ...;
+ QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
+ ...
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName)
+
+ Calls the static method with \a methodName on the class \a className.
+
+ \code
+ QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName)
+
+ Calls the static method with \a methodName on \a clazz.
+
+*/
+
+/*!
+ \fn template <typename T> QJniObject &QJniObject::operator=(T object)
+
+ Replace the current object with \a object. The old Java object will be released.
+*/
+#define MAKE_JNI_OBJECT_METHODS(Type, Signature) \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callObjectMethod<Type>(const char *methodName) const \
+{ \
+ return callObjectMethod(methodName, Signature); \
+} \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(const char *className, \
+ const char *methodName) \
+{ \
+ return callStaticObjectMethod(className, methodName, Signature); \
+} \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(jclass clazz, \
+ const char *methodName) \
+{ \
+ return callStaticObjectMethod(clazz, methodName, Signature); \
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::object<Type>() const\
+{\
+ return static_cast<Type>(javaObject());\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject &QJniObject::operator=(Type obj)\
+{\
+ assign(static_cast<jobject>(obj));\
+ return *this;\
+}
+
+#define DECLARE_JNI_OBJECT_METHODS(Type, Signature) MAKE_JNI_OBJECT_METHODS(Type, Signature)
+
+DECLARE_JNI_OBJECT_METHODS(jobject, "()Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_METHODS(jclass, "()Ljava/lang/Class;")
+DECLARE_JNI_OBJECT_METHODS(jstring, "()Ljava/lang/String;")
+DECLARE_JNI_OBJECT_METHODS(jobjectArray, "()[Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_METHODS(jbooleanArray, "()[Z")
+DECLARE_JNI_OBJECT_METHODS(jbyteArray, "()[B")
+DECLARE_JNI_OBJECT_METHODS(jshortArray, "()[S")
+DECLARE_JNI_OBJECT_METHODS(jintArray, "()[I")
+DECLARE_JNI_OBJECT_METHODS(jlongArray, "()[J")
+DECLARE_JNI_OBJECT_METHODS(jfloatArray, "()[F")
+DECLARE_JNI_OBJECT_METHODS(jdoubleArray, "()[D")
+DECLARE_JNI_OBJECT_METHODS(jcharArray, "()[C")
+DECLARE_JNI_OBJECT_METHODS(jthrowable, "()Ljava/lang/Throwable;")
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, T value);
+
+ Sets the static field with \a fieldName and \a signature to \a value on class named \a className.
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className,
+ const char *fieldName,
+ const char *sig,
+ jobject value)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+
+ if (!clazz)
+ return;
+
+ jfieldID id = getCachedFieldID(env, clazz, className, fieldName, sig, true);
+ if (id) {
+ env->SetStaticObjectField(clazz, id, value);
+ env.exceptionCheckAndClear();
+ }
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, T value);
+
+ Sets the static field with \a fieldName and \a signature to \a value on class \a clazz.
+*/
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz,
+ const char *fieldName,
+ const char *sig,
+ jobject value)
+{
+ QJniEnvironment env;
+ jfieldID id = getFieldID(env, clazz, fieldName, sig, true);
+
+ if (id) {
+ env->SetStaticObjectField(clazz, id, value);
+ env.exceptionCheckAndClear();
+ }
+}
+
+/*!
+ \fn T QJniObject::getField(const char *fieldName) const
+
+ Retrieves the value of the field \a fieldName.
+
+ \code
+ QJniObject volumeControl = ...;
+ jint fieldValue = volumeControl.getField<jint>("MAX_VOLUME");
+ \endcode
+*/
+
+/*!
+ \fn T QJniObject::getStaticField(const char *className, const char *fieldName)
+
+ Retrieves the value from the static field \a fieldName on the class \a className.
+*/
+
+/*!
+ \fn T QJniObject::getStaticField(jclass clazz, const char *fieldName)
+
+ Retrieves the value from the static field \a fieldName on \a clazz.
+*/
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, T value)
+
+ Sets the static field \a fieldName of the class named \a className to \a value.
+*/
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, T value)
+
+ Sets the static field \a fieldName of the class \a clazz to \a value.
+*/
+#define MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) \
+template <> Q_CORE_EXPORT Type QJniObject::getField<Type>(const char *fieldName) const \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ if (id) {\
+ res = env->Get##FieldName##Field(d->m_jobject, id); \
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ } \
+ return res;\
+} \
+template <> \
+Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(const char *className, const char *fieldName) \
+{ \
+ QJniEnvironment env; \
+ jclass clazz = loadClass(className, env); \
+ if (!clazz) \
+ return 0; \
+ jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, \
+ Signature, true); \
+ if (!id) \
+ return 0; \
+ Type res = env->GetStatic##FieldName##Field(clazz, id); \
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ return res;\
+} \
+template <>\
+Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(jclass clazz, const char *fieldName)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ if (id) {\
+ res = env->GetStatic##FieldName##Field(clazz, id);\
+ if (env.exceptionCheckAndClear()) \
+ res = 0; \
+ }\
+ return res;\
+}\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
+ const char *fieldName, \
+ Type value) \
+{ \
+ QJniEnvironment env; \
+ jclass clazz = loadClass(className, env); \
+ if (!clazz) \
+ return; \
+ jfieldID id = getCachedFieldID(env, clazz, className, fieldName, Signature, true); \
+ if (!id) \
+ return; \
+ env->SetStatic##FieldName##Field(clazz, id, value); \
+ env.exceptionCheckAndClear(); \
+}\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(jclass clazz,\
+ const char *fieldName,\
+ Type value)\
+{\
+ QJniEnvironment env;\
+ jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ if (id) {\
+ env->SetStatic##FieldName##Field(clazz, id, value);\
+ env.exceptionCheckAndClear();\
+ }\
+}\
+template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
+{ \
+ QJniEnvironment env; \
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ if (id) { \
+ env->Set##FieldName##Field(d->m_jobject, id, value); \
+ env.exceptionCheckAndClear(); \
+ } \
+} \
+
+#define DECLARE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, \
+ Signature)
+DECLARE_JNI_PRIMITIVE_FIELDS(Boolean, jboolean, "Z")
+DECLARE_JNI_PRIMITIVE_FIELDS(Byte, jbyte, "B")
+DECLARE_JNI_PRIMITIVE_FIELDS(Char, jchar, "C")
+DECLARE_JNI_PRIMITIVE_FIELDS(Short, jshort, "S")
+DECLARE_JNI_PRIMITIVE_FIELDS(Int, jint, "I")
+DECLARE_JNI_PRIMITIVE_FIELDS(Long, jlong, "J")
+DECLARE_JNI_PRIMITIVE_FIELDS(Float, jfloat, "F")
+DECLARE_JNI_PRIMITIVE_FIELDS(Double, jdouble, "D")
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
+ Retrieves the object from the field with \a signature and \a fieldName on class \a className.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField("class/with/Fields", "FIELD_NAME", "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *sig)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (!clazz)
+ return QJniObject();
+ jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName,
+ sig, true);
+ if (!id)
+ return QJniObject();
+
+ jobject res = env->GetStaticObjectField(clazz, id);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, const char *signature)
+ Retrieves the object from the field with \a signature and \a fieldName on \a clazz.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getStaticObjectField(jclass clazz,
+ const char *fieldName,
+ const char *sig)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jfieldID id = getFieldID(env, clazz, fieldName, sig, true);
+ if (id) {
+ res = env->GetStaticObjectField(clazz, id);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves the \a jobject from the field with \a signature and \a fieldName on \a clazz.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz,
+ const char *fieldName,
+ const char *sig)
+{
+ return getStaticObjectField(clazz, fieldName, sig);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves the jobject from the field with \a signature and \a fieldName on class named \a className.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(const char *className,
+ const char *fieldName,
+ const char *sig)
+{
+ return getStaticObjectField(className, fieldName, sig);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves the jobjectArray from the field with \a signature and \a fieldName on \a clazz.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz,
+ const char *fieldName,
+ const char *sig)
+{
+ return getStaticObjectField(clazz, fieldName, sig);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves the jobjectArray from the field with \a signature and \a fieldName on the class named \a className.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className,
+ const char *fieldName,
+ const char *sig)
+{
+ return getStaticObjectField(className, fieldName, sig);
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setField(const char *fieldName, const char *signature, T value)
+
+ Sets the value of \a fieldName with \a signature to \a value.
+
+ \code
+ QJniObject stringArray = ...;
+ QJniObject obj = ...;
+ obj.setField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V", stringArray.object<jobjectArray>())
+ \endcode
+*/
+template <> Q_CORE_EXPORT
+void QJniObject::setField<jobject>(const char *fieldName, const char *sig, jobject value)
+{
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig);
+ if (id) {
+ env->SetObjectField(d->m_jobject, id, value);
+ env.exceptionCheckAndClear();
+ }
+}
+
+template <> Q_CORE_EXPORT
+void QJniObject::setField<jobjectArray>(const char *fieldName, const char *sig, jobjectArray value)
+{
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig);
+ if (id) {
+ env->SetObjectField(d->m_jobject, id, value);
+ env.exceptionCheckAndClear();
+ }
+}
+
+/*!
+ \fn QJniObject QJniObject::getObjectField(const char *fieldName) const
+
+ Retrieves the object of field \a fieldName.
+
+ \code
+ QJniObject field = jniObject.getObjectField<jstring>("FIELD_NAME");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
+
+ Retrieves the object from the field with \a signature and \a fieldName.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject field = jniObject.getObjectField("FIELD_NAME", "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getObjectField(const char *fieldName, const char *sig) const
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig);
+ if (id) {
+ res = env->GetObjectField(d->m_jobject, id);
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setField(const char *fieldName, T value)
+
+ Sets the value of \a fieldName to \a value.
+
+ \code
+ ...
+ QJniObject obj;
+ obj.setField<jint>("AN_INT_FIELD", 10);
+ jstring myString = ...
+ obj.setField<jstring>("A_STRING_FIELD", myString);
+ ...
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
+
+ Retrieves the object from the field \a fieldName on the class \a className.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField<jstring>("class/with/Fields", "FIELD_NAME");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
+
+ Retrieves the object from the field \a fieldName on \a clazz.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField<jstring>(clazz, "FIELD_NAME");
+ \endcode
+*/
+
+#define MAKE_JNI_OBJECT_FILEDS(Type, Signature) \
+template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
+{ \
+ QJniObject::setField<jobject>(fieldName, Signature, value); \
+} \
+\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
+ const char *fieldName, \
+ Type value) \
+{ \
+ QJniObject::setStaticField<jobject>(className, fieldName, Signature, value); \
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getObjectField<Type>(const char *fieldName) const\
+{\
+ return getObjectField(fieldName, Signature);\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(jclass clazz,\
+ const char *fieldName)\
+{\
+ return getStaticObjectField(clazz, fieldName, Signature);\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(const char *className,\
+ const char *fieldName)\
+{\
+ return getStaticObjectField(className, fieldName, Signature);\
+}\
+
+#define DECLARE_JNI_OBJECT_FILEDS(Type, Signature) MAKE_JNI_OBJECT_FILEDS(Type, Signature)
+
+DECLARE_JNI_OBJECT_FILEDS(jobject, "Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_FILEDS(jobjectArray, "[Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_FILEDS(jstring, "Ljava/lang/String;")
+DECLARE_JNI_OBJECT_FILEDS(jclass, "Ljava/lang/Class;")
+DECLARE_JNI_OBJECT_FILEDS(jthrowable, "Ljava/lang/Throwable;")
+DECLARE_JNI_OBJECT_FILEDS(jbooleanArray, "[Z")
+DECLARE_JNI_OBJECT_FILEDS(jbyteArray, "[B")
+DECLARE_JNI_OBJECT_FILEDS(jcharArray, "[C")
+DECLARE_JNI_OBJECT_FILEDS(jshortArray, "[S")
+DECLARE_JNI_OBJECT_FILEDS(jintArray, "[I")
+DECLARE_JNI_OBJECT_FILEDS(jlongArray, "[J")
+DECLARE_JNI_OBJECT_FILEDS(jfloatArray, "[F")
+DECLARE_JNI_OBJECT_FILEDS(jdoubleArray, "[D")
+
+/*!
+ \fn QJniObject QJniObject::fromString(const QString &string)
+
+ Creates a Java string from the QString \a string and returns a QJniObject holding that string.
+
+ \code
+ QString myQString = "QString";
+ QJniObject myJavaString = QJniObject::fromString(myQString);
+ \endcode
+
+ \sa toString()
+*/
+QJniObject QJniObject::fromString(const QString &string)
+{
+ QJniEnvironment env;
+ jstring res = env->NewString(reinterpret_cast<const jchar*>(string.constData()),
+ string.length());
+
+ if (env.exceptionCheckAndClear()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+/*!
+ \fn QString QJniObject::toString() const
+
+ Returns a QString with a string representation of the java object.
+ Calling this function on a Java String object is a convenient way of getting the actual string
+ data.
+
+ \code
+ QJniObject string = ...; // "Hello Java"
+ QString qstring = string.toString(); // "Hello Java"
+ \endcode
+
+ \sa fromString()
+*/
+QString QJniObject::toString() const
+{
+ if (!isValid())
+ return QString();
+
+ QJniObject string = callObjectMethod<jstring>("toString");
+ return qt_convertJString(static_cast<jstring>(string.object()));
+}
+
+/*!
+ \fn bool QJniObject::isClassAvailable(const char *className)
+
+ Returns true if the Java class \a className is available.
+
+ \code
+ if (QJniObject::isClassAvailable("java/lang/String")) {
+ // condition statement
+ }
+ \endcode
+*/
+bool QJniObject::isClassAvailable(const char *className)
+{
+ QJniEnvironment env;
+
+ if (!env)
+ return false;
+
+ return loadClass(className, env);;
+}
+
+/*!
+ \fn bool QJniObject::isValid() const
+
+ Returns true if this instance holds a valid Java object.
+
+ \code
+ QJniObject qjniObject; ==> isValid() == false
+ QJniObject qjniObject(0) ==> isValid() == false
+ QJniObject qjniObject("could/not/find/Class") ==> isValid() == false
+ \endcode
+*/
+bool QJniObject::isValid() const
+{
+ return d->m_jobject;
+}
+
+/*!
+ \fn QJniObject QJniObject::fromLocalRef(jobject localRef)
+ \since 6.1
+
+ Creates a QJniObject from the local JNI reference \a localRef.
+ This function takes ownership of \a localRef and frees it before returning.
+
+ \note Only call this function with a local JNI reference. For example, most raw JNI calls,
+ through the JNI environment, return local references to a java object.
+
+ \code
+ jobject localRef = env->GetObjectArrayElement(array, index);
+ QJniObject element = QJniObject::fromLocalRef(localRef);
+ \endcode
+*/
+QJniObject QJniObject::fromLocalRef(jobject lref)
+{
+ QJniObject obj(lref);
+ QJniEnvironment()->DeleteLocalRef(lref);
+ return obj;
+}
+
+bool QJniObject::isSameObject(jobject obj) const
+{
+ return QJniEnvironment()->IsSameObject(d->m_jobject, obj);
+}
+
+bool QJniObject::isSameObject(const QJniObject &other) const
+{
+ return isSameObject(other.d->m_jobject);
+}
+
+void QJniObject::assign(jobject obj)
+{
+ if (isSameObject(obj))
+ return;
+
+ jobject jobj = static_cast<jobject>(obj);
+ d = QSharedPointer<QJniObjectPrivate>::create();
+ if (obj) {
+ QJniEnvironment env;
+ d->m_jobject = env->NewGlobalRef(jobj);
+ jclass objectClass = env->GetObjectClass(jobj);
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
+ env->DeleteLocalRef(objectClass);
+ }
+}
+
+jobject QJniObject::javaObject() const
+{
+ return d->m_jobject;
+}
+
+QT_END_NAMESPACE