JNI: add a static getter for a JNIEnv pointer to QJniEnvironment

This further helps reduce the creation of temporary QJniEnivronment
instances (with allocated d-pointer) for cases where we simply need to
get the JNIEnv for the current thread.

Change-Id: I2eda238124be51c755d8910de9dbc9ca8eb92288
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
Reviewed-by: Petri Virkkunen <petri.virkkunen@qt.io>
bb10
Volker Hilsheimer 2023-10-12 16:03:17 +02:00
parent e21d35b9f7
commit 6735aa868f
5 changed files with 65 additions and 60 deletions

View File

@ -278,16 +278,15 @@ template <typename ElementType, typename List, typename NewFn, typename SetFn>
auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion)
{
const int length = int(list.size());
QJniEnvironment env;
JNIEnv *jenv = env.jniEnv();
auto localArray = (jenv->*newArray)(length);
if (env.checkAndClearExceptions())
JNIEnv *env = QJniEnvironment::getJniEnv();
auto localArray = (env->*newArray)(length);
if (QJniEnvironment::checkAndClearExceptions(env))
return QJniArray<ElementType>();
// can't use static_cast here because we have signed/unsigned mismatches
if (length) {
(jenv->*setRegion)(localArray, 0, length,
reinterpret_cast<const ElementType *>(std::as_const(list).data()));
(env->*setRegion)(localArray, 0, length,
reinterpret_cast<const ElementType *>(std::as_const(list).data()));
}
return QJniArray<ElementType>(localArray);
};
@ -299,7 +298,7 @@ auto QJniArrayBase::makeObjectArray(List &&list)
if (list.isEmpty())
return QJniArray<jobject>();
QJniEnvironment env;
JNIEnv *env = QJniEnvironment::getJniEnv();
const int length = int(list.size());
// this assumes that all objects in the list have the same class
@ -309,7 +308,7 @@ auto QJniArrayBase::makeObjectArray(List &&list)
else
elementClass = env->GetObjectClass(list.first());
auto localArray = env->NewObjectArray(length, elementClass, nullptr);
if (env.checkAndClearExceptions())
if (QJniEnvironment::checkAndClearExceptions(env))
return QJniArray<jobject>();
for (int i = 0; i < length; ++i) {
jobject object;

View File

@ -53,10 +53,20 @@ Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
QJniEnvironment::QJniEnvironment()
: d(new QJniEnvironmentPrivate{})
{
d->jniEnv = getJniEnv();
}
/*!
Returns the JNIEnv pointer for the current thread.
The current thread will be attached to the Java VM.
*/
JNIEnv *QJniEnvironment::getJniEnv()
{
JNIEnv *jniEnv = nullptr;
JavaVM *vm = QtAndroidPrivate::javaVM();
const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
if (ret == JNI_OK) // Already attached
return;
const jint ret = vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
if (ret == JNI_EDETACHED) { // We need to (re-)attach
const QByteArray threadName = QThread::currentThread()->objectName().toUtf8();
@ -64,12 +74,12 @@ QJniEnvironment::QJniEnvironment()
threadName.isEmpty() ? "QtThread" : threadName.constData(),
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);
if (vm->AttachCurrentThread(&jniEnv, &args) == JNI_OK) {
if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
}
}
return jniEnv;
}
/*!

View File

@ -78,6 +78,8 @@ public:
bool checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose);
static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
static JNIEnv *getJniEnv();
private:
Q_DISABLE_COPY_MOVE(QJniEnvironment)
QScopedPointer<QJniEnvironmentPrivate> d;

View File

@ -24,7 +24,7 @@ using namespace Qt::StringLiterals;
\brief A convenience wrapper around the Java Native Interface (JNI).
The QJniObject class wraps a reference to a Java object, ensuring it isn't
gargage-collected and providing access to most \c JNIEnv method calls
garbage-collected and providing access to most \c JNIEnv method calls
(member, static) and fields (setter, getter). It eliminates much
boiler-plate that would normally be needed, with direct JNI access, for
every operation, including exception-handling.
@ -281,20 +281,17 @@ using namespace Qt::StringLiterals;
class QJniObjectPrivate
{
public:
// This is safe and necessary - the JNIEnv is attached to the current thread
// and the pointer remains valid for as long as the thread is alive. And if the
// QJniObject outlives the thread that it lives in, and gets called for
// anything other than destruction, then we have a data race anyway.
// And it's necessary, because the QJniEnvironment destructor calls
// checkAndClearExceptions, and since we have QJniObjects that get destroyed from
// a different thread than the one "owning" it, this triggers JNI assertions.
// The JNIEnv is attached to the current thread, and the pointer remains
// valid for as long as the thread is alive. If the QJniObject were to
// outlive the thread that it lives in, and gets called for anything other
// than destruction, then we have a data race anyway.
QJniObjectPrivate()
: m_env(QJniEnvironment().jniEnv())
: m_env(QJniEnvironment::getJniEnv())
{
}
~QJniObjectPrivate() {
// use the environment of the current thread here
QJniEnvironment env;
JNIEnv *env = QJniEnvironment::getJniEnv();
if (m_jobject)
env->DeleteGlobalRef(m_jobject);
if (m_jclass && m_own_jclass)
@ -1035,16 +1032,16 @@ QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sign
QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...)
{
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
JNIEnv *env = QJniEnvironment::getJniEnv();
jclass clazz = QJniObject::loadClass(className, env);
if (clazz) {
jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz,
jmethodID id = QJniObject::getCachedMethodID(env, clazz,
className,
methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env.jniEnv());
QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env);
va_end(args);
return res;
}
@ -1232,18 +1229,18 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
const char *fieldName,
const char *signature)
{
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
JNIEnv *env = QJniEnvironment::getJniEnv();
jclass clazz = QJniObject::loadClass(className, env);
if (!clazz)
return QJniObject();
jfieldID id = QJniObject::getCachedFieldID(env.jniEnv(), clazz,
jfieldID id = QJniObject::getCachedFieldID(env, clazz,
className,
fieldName,
signature, true);
if (!id)
return QJniObject();
return getCleanJniObject(env->GetStaticObjectField(clazz, id), env.jniEnv());
return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
}
/*!
@ -1261,12 +1258,9 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature)
{
QJniEnvironment env;
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (!id)
return QJniObject();
return getCleanJniObject(env->GetStaticObjectField(clazz, id), env.jniEnv());
JNIEnv *env = QJniEnvironment::getJniEnv();
jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
}
/*!

View File

@ -47,7 +47,7 @@ class Q_CORE_EXPORT QJniObject
JNIEnv *jniEnv() const
{
if (!env)
env = QJniEnvironment().jniEnv();
env = QJniEnvironment::getJniEnv();
return env;
}
bool checkAndClearExceptions()
@ -176,16 +176,16 @@ public:
template <typename Ret, typename ...Args>
static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
{
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
JNIEnv *env = QJniEnvironment::getJniEnv();
jclass clazz = QJniObject::loadClass(className, env);
return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
static auto callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
{
QJniEnvironment env;
jmethodID id = clazz ? getMethodID(env.jniEnv(), clazz, methodName, signature, true)
JNIEnv *env = QJniEnvironment::getJniEnv();
jmethodID id = clazz ? getMethodID(env, clazz, methodName, signature, true)
: 0;
return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
}
@ -228,9 +228,9 @@ public:
>
static auto callStaticMethod(const char *className, const char *methodName, Args &&...args)
{
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
const jmethodID id = clazz ? getMethodID(env.jniEnv(), clazz, methodName,
JNIEnv *env = QJniEnvironment::getJniEnv();
jclass clazz = QJniObject::loadClass(className, env);
const jmethodID id = clazz ? getMethodID(env, clazz, methodName,
QtJniTypes::methodSignature<Ret, Args...>().data(), true)
: 0;
return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
@ -253,10 +253,10 @@ public:
>
static auto callStaticMethod(const char *methodName, Args &&...args)
{
QJniEnvironment env;
JNIEnv *env = QJniEnvironment::getJniEnv();
const jclass clazz = QJniObject::loadClass(QtJniTypes::Traits<Klass>::className().data(),
env.jniEnv());
const jmethodID id = clazz ? getMethodID(env.jniEnv(), clazz, methodName,
env);
const jmethodID id = clazz ? getMethodID(env, clazz, methodName,
QtJniTypes::methodSignature<Ret, Args...>().data(), true)
: 0;
return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
@ -474,17 +474,17 @@ public:
static void setStaticField(const char *className, const char *fieldName,
const char *signature, T value)
{
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
JNIEnv *env = QJniEnvironment::getJniEnv();
jclass clazz = QJniObject::loadClass(className, env);
if (!clazz)
return;
jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
jfieldID id = getCachedFieldID(env, clazz, className, fieldName,
signature, true);
if (id) {
setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
env.checkAndClearExceptions();
setStaticFieldForType<T>(env, clazz, id, value);
QJniEnvironment::checkAndClearExceptions(env);
}
}
@ -496,12 +496,12 @@ public:
static void setStaticField(jclass clazz, const char *fieldName,
const char *signature, T value)
{
QJniEnvironment env;
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
JNIEnv *env = QJniEnvironment::getJniEnv();
jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
if (id) {
setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
env.checkAndClearExceptions();
setStaticFieldForType<T>(env, clazz, id, value);
QJniEnvironment::checkAndClearExceptions(env);
}
}