summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjniobject.cpp
diff options
context:
space:
mode:
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