summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
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
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')
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp16
-rw-r--r--src/corelib/kernel/qjni_p.h1
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp283
-rw-r--r--src/corelib/kernel/qjnienvironment.h85
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp74
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h1
-rw-r--r--src/corelib/kernel/qjniobject.cpp1872
-rw-r--r--src/corelib/kernel/qjniobject.h207
-rw-r--r--src/corelib/kernel/qjnionload.cpp5
9 files changed, 2496 insertions, 48 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index d9a74c2828..781791c971 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -91,8 +91,8 @@
#endif // QT_NO_QOBJECT
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-# include <private/qjni_p.h>
-# include <private/qjnihelpers_p.h>
+#include <QJniObject>
+#include <private/qjnihelpers_p.h>
#endif
#ifdef Q_OS_MAC
@@ -170,17 +170,17 @@ QString QCoreApplicationPrivate::appVersion() const
# ifdef Q_OS_DARWIN
applicationVersion = infoDictionaryStringProperty(QStringLiteral("CFBundleVersion"));
# elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
- QJNIObjectPrivate context(QtAndroidPrivate::context());
+ QJniObject context(QtAndroidPrivate::context());
if (context.isValid()) {
- QJNIObjectPrivate pm = context.callObjectMethod(
+ QJniObject pm = context.callObjectMethod(
"getPackageManager", "()Landroid/content/pm/PackageManager;");
- QJNIObjectPrivate pn = context.callObjectMethod<jstring>("getPackageName");
+ QJniObject pn = context.callObjectMethod<jstring>("getPackageName");
if (pm.isValid() && pn.isValid()) {
- QJNIObjectPrivate packageInfo = pm.callObjectMethod(
+ QJniObject packageInfo = pm.callObjectMethod(
"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",
pn.object(), 0);
if (packageInfo.isValid()) {
- QJNIObjectPrivate versionName = packageInfo.getObjectField(
+ QJniObject versionName = packageInfo.getObjectField(
"versionName", "Ljava/lang/String;");
if (versionName.isValid())
return versionName.toString();
diff --git a/src/corelib/kernel/qjni_p.h b/src/corelib/kernel/qjni_p.h
index edca36e2bd..57ec8a39b5 100644
--- a/src/corelib/kernel/qjni_p.h
+++ b/src/corelib/kernel/qjni_p.h
@@ -47,6 +47,7 @@
//
// We mean it.
//
+// FIXME: Remove this once the JNI API is used by other modules.
#ifndef QJNI_P_H
#define QJNI_P_H
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
new file mode 100644
index 0000000000..04676cc29c
--- /dev/null
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** 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 "qjniobject.h"
+#include "qjnihelpers_p.h"
+
+#include <QtCore/QThread>
+#include <QtCore/QThreadStorage>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QJniEnvironment
+ \inmodule QtCore
+ \brief The QJniEnvironment provides access to the JNI Environment.
+ \since 6.1
+
+ \note This API has been tested and meant to be mainly used for Android and it hasn't been tested
+ for other platforms.
+*/
+
+static const char qJniThreadName[] = "QtThread";
+
+class QJniEnvironmentPrivate
+{
+public:
+ JNIEnv *jniEnv = nullptr;
+};
+
+class QJniEnvironmentPrivateTLS
+{
+public:
+ inline ~QJniEnvironmentPrivateTLS()
+ {
+ QtAndroidPrivate::javaVM()->DetachCurrentThread();
+ }
+};
+
+struct QJniLocalRefDeleterPrivate
+{
+ static void cleanup(jobject obj)
+ {
+ if (!obj)
+ return;
+
+ QJniEnvironment env;
+ env->DeleteLocalRef(obj);
+ }
+};
+
+// To simplify this we only define it for jobjects.
+typedef QScopedPointer<_jobject, QJniLocalRefDeleterPrivate> QJniScopedLocalRefPrivate;
+
+
+Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
+
+
+
+/*!
+ \fn QJniEnvironment::QJniEnvironment()
+
+ Constructs a new QJniEnvironment object and attaches the current thread to the Java VM.
+*/
+QJniEnvironment::QJniEnvironment()
+ : d(new QJniEnvironmentPrivate{})
+{
+ JavaVM *vm = QtAndroidPrivate::javaVM();
+ const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
+ if (ret == JNI_OK) // Already attached
+ return;
+
+ if (ret == JNI_EDETACHED) { // We need to (re-)attach
+ JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr };
+ if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK)
+ return;
+
+ if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
+ jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ }
+}
+
+/*!
+ \fn QJniEnvironment::~QJniEnvironment()
+
+ Detaches the current thread from the Java VM and destroys the QJniEnvironment object.
+*/
+QJniEnvironment::~QJniEnvironment()
+{
+ exceptionCheckAndClear();
+}
+
+/*!
+ \fn JNIEnv *QJniEnvironment::operator->()
+
+ Provides access to the QJniEnvironment's JNIEnv pointer.
+*/
+JNIEnv *QJniEnvironment::operator->()
+{
+ return d->jniEnv;
+}
+
+/*!
+ \fn QJniEnvironment::operator JNIEnv *() const
+
+ Returns the JNI Environment pointer.
+*/
+QJniEnvironment::operator JNIEnv* () const
+{
+ return d->jniEnv;
+}
+
+/*!
+ \fn jclass QJniEnvironment::findClass(const char *className)
+
+ Searches for \a className using all available class loaders. Qt on Android
+ uses a custom class loader to load all the .jar files and it must be used
+ to find any classes that are created by that class loader because these
+ classes are not visible in the default class loader.
+
+ Returns the class pointer or null if is not found.
+
+ A use case for this function is searching for a custom class then calling
+ its memeber method. The following code snippet create an instance of the
+ class \c CustomClass and then calls \c printFromJava() method:
+
+ \code
+ QJniEnvironment env;
+ jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
+ QJniObject classObject(javaClass);
+
+ QJniObject javaMessage = QJniObject::fromString("findClass example");
+ classObject.callMethod<void>("printFromJava",
+ "(Ljava/lang/String;)V",
+ javaMessage.object<jstring>());
+ \endcode
+
+ \since Qt 6.1
+*/
+jclass QJniEnvironment::findClass(const char *className)
+{
+ return QtAndroidPrivate::findClass(className, d->jniEnv);
+}
+
+/*!
+ \fn JavaVM *QJniEnvironment::javaVM()
+
+ Returns the Java VM interface.
+
+ \since Qt 6.1
+*/
+JavaVM *QJniEnvironment::javaVM()
+{
+ return QtAndroidPrivate::javaVM();
+}
+
+/*!
+ \fn bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[])
+
+ Registers the Java methods \a methods that can call native C++ functions from class \a
+ className. These methods must be registered before any attempt to call them.
+
+ Returns True if the registration is successful, otherwise False.
+
+ Each element in the methods array consists of:
+ \list
+ \li The Java method name
+ \li Method signature
+ \li The C++ functions that will be executed
+ \endlist
+
+ \code
+ JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)},
+ {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}};
+ QJniEnvironment env;
+ env.registerNativeMethods("org/qtproject/android/TestJavaClass", methods);
+ \endcode
+
+ \since Qt 6.1
+*/
+bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[], int size)
+{
+ jclass clazz = findClass(className);
+
+ if (!clazz)
+ return false;
+
+ jclass gClazz = static_cast<jclass>(d->jniEnv->NewGlobalRef(clazz));
+
+ if (d->jniEnv->RegisterNatives(gClazz, methods, size / sizeof(methods[0])) < 0) {
+ exceptionCheckAndClear();
+ return false;
+ }
+
+ d->jniEnv->DeleteLocalRef(gClazz);
+
+ return true;
+}
+
+/*!
+ \enum QJniExceptionCleaner::OutputMode
+
+ \value Silent the exceptions are cleaned silently
+ \value Verbose describes the exceptions before cleaning them
+*/
+
+/*!
+ \fn QJniEnvironment::exceptionCheckAndClear(OutputMode outputMode = OutputMode::Silent)
+
+ Cleans any pending exceptions either silently or with descriptions, depending on the \a outputMode.
+
+ \since 6.1
+*/
+bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputMode)
+{
+ if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
+ if (outputMode != OutputMode::Silent)
+ d->jniEnv->ExceptionDescribe();
+ d->jniEnv->ExceptionClear();
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \fn QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Silent)
+
+ Cleans any pending exceptions for \a env, either silently or with descriptions, depending on the \a outputMode.
+
+ \since 6.1
+*/
+bool QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
+{
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+ if (outputMode != OutputMode::Silent)
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ return true;
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
new file mode 100644
index 0000000000..e19a3935da
--- /dev/null
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QJNI_ENVIRONMENT_H
+#define QJNI_ENVIRONMENT_H
+
+#include <QScopedPointer>
+
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#include <jni.h>
+#else
+class JNIEnv;
+class JNINativeMethod;
+class JavaVM;
+class jclass;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QJniEnvironmentPrivate;
+
+class Q_CORE_EXPORT QJniEnvironment
+{
+public:
+ QJniEnvironment();
+ ~QJniEnvironment();
+ JNIEnv *operator->();
+ operator JNIEnv *() const;
+ jclass findClass(const char *className);
+ static JavaVM *javaVM();
+ bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size);
+
+ enum class OutputMode {
+ Silent,
+ Verbose
+ };
+
+ bool exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose);
+ static bool exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
+
+
+private:
+ Q_DISABLE_COPY_MOVE(QJniEnvironment)
+ QScopedPointer<QJniEnvironmentPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QJNI_ENVIRONMENT_H
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 14f9f389e8..45f4a4d895 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -37,14 +37,16 @@
**
****************************************************************************/
+#include "qcoreapplication.h"
+#include "qjnienvironment.h"
#include "qjnihelpers_p.h"
-#include "qjni_p.h"
-#include "qmutex.h"
+#include "qjniobject.h"
#include "qlist.h"
+#include "qmutex.h"
#include "qsemaphore.h"
#include "qsharedpointer.h"
#include "qthread.h"
-#include "qcoreapplication.h"
+
#include <QtCore/qrunnable.h>
#include <deque>
@@ -146,7 +148,7 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requ
std::unique_ptr<jint[]> results(new jint[size]);
env->GetIntArrayRegion(grantResults, 0, size, results.get());
for (int i = 0 ; i < size; ++i) {
- const auto &permission = QJNIObjectPrivate(env->GetObjectArrayElement(permissions, i)).toString();
+ const auto &permission = QJniObject(env->GetObjectArrayElement(permissions, i)).toString();
auto value = results[i] == PERMISSION_GRANTED ?
QtAndroidPrivate::PermissionsResult::Granted :
QtAndroidPrivate::PermissionsResult::Denied;
@@ -286,27 +288,14 @@ void QtAndroidPrivate::handleResume()
listeners.at(i)->handleResume();
}
-static inline bool exceptionCheck(JNIEnv *env)
-{
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
static void setAndroidSdkVersion(JNIEnv *env)
{
jclass androidVersionClass = env->FindClass("android/os/Build$VERSION");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return;
jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return;
g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID);
@@ -342,42 +331,42 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
{
jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
"activity",
"()Landroid/app/Activity;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
"service",
"()Landroid/app/Service;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
"classLoader",
"()Ljava/lang/ClassLoader;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
setAndroidSdkVersion(env);
@@ -405,7 +394,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
- if (!regOk && exceptionCheck(env))
+ if (!regOk && QJniEnvironment::exceptionCheckAndClear(env))
return JNI_ERR;
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
@@ -495,7 +484,10 @@ void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &
waitForSemaphore(timeoutMs, sem);
}
-void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permissions, const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, bool directCall)
+void QtAndroidPrivate::requestPermissions(JNIEnv *env,
+ const QStringList &permissions,
+ const QtAndroidPrivate::PermissionsResultFunc &callbackFunc,
+ bool directCall)
{
if (androidSdkVersion() < 23 || !activity()) {
QHash<QString, QtAndroidPrivate::PermissionsResult> res;
@@ -517,12 +509,17 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
- QJNIEnvironmentPrivate env;
- auto array = env->NewObjectArray(permissions.size(), env->FindClass("java/lang/String"), nullptr);
+ QJniEnvironment env;
+ jclass clazz = env->FindClass("java/lang/String");
+
+ if (env.exceptionCheckAndClear())
+ return;
+
+ auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
int index = 0;
for (const auto &perm : permissions)
- env->SetObjectArrayElement(array, index++, QJNIObjectPrivate::fromString(perm).object());
- QJNIObjectPrivate(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
+ env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
+ QJniObject(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
env->DeleteLocalRef(array);
}, env);
}
@@ -543,10 +540,10 @@ QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEn
QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission)
{
- const auto res = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
- "checkSelfPermission",
- "(Ljava/lang/String;)I",
- QJNIObjectPrivate::fromString(permission).object());
+ const auto res = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
+ "checkSelfPermission",
+ "(Ljava/lang/String;)I",
+ QJniObject::fromString(permission).object());
return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied;
}
@@ -555,8 +552,9 @@ bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permi
if (androidSdkVersion() < 23 || !activity())
return false;
- return QJNIObjectPrivate(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", "(Ljava/lang/String;)Z",
- QJNIObjectPrivate::fromString(permission).object());
+ return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale",
+ "(Ljava/lang/String;)Z",
+ QJniObject::fromString(permission).object());
}
void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index c849013ba2..a0d9c219d7 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -119,6 +119,7 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT jobject context();
Q_CORE_EXPORT JavaVM *javaVM();
Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env);
+ Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
jobject classLoader();
Q_CORE_EXPORT jint androidSdkVersion();
Q_CORE_EXPORT void runOnAndroidThread(const Runnable &runnable, JNIEnv *env);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
new file mode 100644
index 0000000000..ba7d0af778
--- /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
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
new file mode 100644
index 0000000000..2998c7b4e8
--- /dev/null
+++ b/src/corelib/kernel/qjniobject.h
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QJNIOBJECT_H
+#define QJNIOBJECT_H
+
+#include <QtCore/QSharedPointer>
+
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#include <jni.h>
+#else
+class jclass;
+class jobject;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QJniObjectPrivate;
+
+class Q_CORE_EXPORT QJniObject
+{
+public:
+ QJniObject();
+ explicit QJniObject(const char *className);
+ explicit QJniObject(const char *className, const char *sig, ...);
+ explicit QJniObject(jclass clazz);
+ explicit QJniObject(jclass clazz, const char *sig, ...);
+ QJniObject(jobject globalRef);
+ ~QJniObject();
+
+ template <typename T>
+ T object() const;
+ jobject object() const;
+
+ template <typename T>
+ T callMethod(const char *methodName, const char *sig, ...) const;
+ template <typename T>
+ T callMethod(const char *methodName) const;
+ template <typename T>
+ QJniObject callObjectMethod(const char *methodName) const;
+ QJniObject callObjectMethod(const char *methodName, const char *sig, ...) const;
+
+ template <typename T>
+ static T callStaticMethod(const char *className, const char *methodName, const char *sig, ...);
+ template <typename T>
+ static T callStaticMethod(const char *className, const char *methodName);
+ template <typename T>
+ static T callStaticMethod(jclass clazz, const char *methodName, const char *sig, ...);
+ template <typename T>
+ static T callStaticMethod(jclass clazz, const char *methodName);
+
+ template <typename T>
+ static QJniObject callStaticObjectMethod(const char *className, const char *methodName);
+ static QJniObject callStaticObjectMethod(const char *className,
+ const char *methodName,
+ const char *sig, ...);
+
+ template <typename T>
+ static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName);
+ static QJniObject callStaticObjectMethod(jclass clazz,
+ const char *methodName,
+ const char *sig, ...);
+
+ template <typename T>
+ T getField(const char *fieldName) const;
+
+ template <typename T>
+ static T getStaticField(const char *className, const char *fieldName);
+ template <typename T>
+ static T getStaticField(jclass clazz, const char *fieldName);
+
+ template <typename T>
+ QJniObject getObjectField(const char *fieldName) const;
+ QJniObject getObjectField(const char *fieldName, const char *sig) const;
+
+ template <typename T>
+ static QJniObject getStaticObjectField(const char *className, const char *fieldName);
+ static QJniObject getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *sig);
+ template <typename T>
+ static QJniObject getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *sig);
+
+ template <typename T>
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName);
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *sig);
+ template <typename T>
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *sig);
+
+ template <typename T>
+ void setField(const char *fieldName, T value);
+ template <typename T>
+ void setField(const char *fieldName, const char *sig, T value);
+ template <typename T>
+ static void setStaticField(const char *className, const char *fieldName, T value);
+ template <typename T>
+ static void setStaticField(const char *className, const char *fieldName,
+ const char *sig, T value);
+ template <typename T>
+ static void setStaticField(jclass clazz, const char *fieldName, const char *sig, T value);
+
+ template <typename T>
+ static void setStaticField(jclass clazz, const char *fieldName, T value);
+
+ static QJniObject fromString(const QString &string);
+ QString toString() const;
+
+ static bool isClassAvailable(const char *className);
+ bool isValid() const;
+
+ // This function takes ownership of the jobject and releases the local ref. before returning.
+ static QJniObject fromLocalRef(jobject lref);
+
+ template <typename T> QJniObject &operator=(T obj);
+
+private:
+ struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; };
+
+ QJniObject(const char *className, const char *sig, const QVaListPrivate &args);
+ QJniObject(jclass clazz, const char *sig, const QVaListPrivate &args);
+
+ template <typename T>
+ T callMethodV(const char *methodName, const char *sig, va_list args) const;
+ QJniObject callObjectMethodV(const char *methodName, const char *sig, va_list args) const;
+ template <typename T>
+ static T callStaticMethodV(const char *className,
+ const char *methodName,
+ const char *sig,
+ va_list args);
+ template <typename T>
+ static T callStaticMethodV(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ va_list args);
+
+ static QJniObject callStaticObjectMethodV(const char *className,
+ const char *methodName,
+ const char *sig,
+ va_list args);
+
+ static QJniObject callStaticObjectMethodV(jclass clazz,
+ const char *methodName,
+ const char *sig,
+ va_list args);
+
+ bool isSameObject(jobject obj) const;
+ bool isSameObject(const QJniObject &other) const;
+ void assign(jobject obj);
+ jobject javaObject() const;
+
+ friend bool operator==(const QJniObject &, const QJniObject &);
+ friend bool operator!=(const QJniObject&, const QJniObject&);
+
+ QSharedPointer<QJniObjectPrivate> d;
+};
+
+inline bool operator==(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return obj1.isSameObject(obj2);
+}
+
+inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return !obj1.isSameObject(obj2);
+}
+
+QT_END_NAMESPACE
+
+#endif // QJNIOBJECT_H
diff --git a/src/corelib/kernel/qjnionload.cpp b/src/corelib/kernel/qjnionload.cpp
index ec56d3de66..dbaee48ac6 100644
--- a/src/corelib/kernel/qjnionload.cpp
+++ b/src/corelib/kernel/qjnionload.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -37,8 +37,9 @@
**
****************************************************************************/
-#include <jni.h>
#include "qjnihelpers_p.h"
+
+#include <jni.h>
#include <android/log.h>
static const char logTag[] = "QtCore";