Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qjniobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qjniobject.h"
5
6#include "qjnihelpers_p.h"
7
8#include <QtCore/qbytearray.h>
9#include <QtCore/qhash.h>
10#include <QtCore/qreadwritelock.h>
11
13
14using namespace Qt::StringLiterals;
15
278{
279public:
280 QJniObjectPrivate() = default;
282 QJniEnvironment env;
283 if (m_jobject)
284 env->DeleteGlobalRef(m_jobject);
285 if (m_jclass && m_own_jclass)
286 env->DeleteGlobalRef(m_jclass);
287 }
288
289 friend jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env);
290 static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
291 {
292 return QJniObject::loadClass(className, env, binEncoded);
293 }
294
296 {
297 return QJniObject::toBinaryEncClassName(className);
298 }
299
300 jobject m_jobject = nullptr;
301 jclass m_jclass = nullptr;
302 bool m_own_jclass = true;
304};
305
307{
308 return "%1%2:%3"_L1;
309}
310
311static QString qt_convertJString(jstring string)
312{
313 QJniEnvironment env;
314 int strLength = env->GetStringLength(string);
315 QString res(strLength, Qt::Uninitialized);
316 env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data()));
317 return res;
318}
319
321Q_GLOBAL_STATIC(JClassHash, cachedClasses)
322Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
323
324static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr)
325{
326 QReadLocker locker(cachedClassesLock);
327 const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(QString::fromLatin1(classBinEnc));
328 const bool found = (it != cachedClasses->constEnd());
329
330 if (isCached)
331 *isCached = found;
332
333 return found ? it.value() : 0;
334}
335
336QByteArray QJniObject::toBinaryEncClassName(const QByteArray &className)
337{
338 return QByteArray(className).replace('/', '.');
339}
340
341jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded)
342{
343 const QByteArray &binEncClassName = binEncoded ? className : QJniObject::toBinaryEncClassName(className);
344
345 bool isCached = false;
346 jclass clazz = getCachedClass(binEncClassName, &isCached);
347 if (clazz || isCached)
348 return clazz;
349
351 if (!classLoader.isValid())
352 return nullptr;
353
354 QWriteLocker locker(cachedClassesLock);
355 // did we lose the race?
356 const QLatin1StringView key(binEncClassName);
358 if (it != cachedClasses->constEnd())
359 return it.value();
360
361 QJniObject stringName = QJniObject::fromString(key);
362 QJniObject classObject = classLoader.callObjectMethod("loadClass",
363 "(Ljava/lang/String;)Ljava/lang/Class;",
364 stringName.object());
365
366 if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
367 clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
368
369 cachedClasses->insert(key, clazz);
370 return clazz;
371}
372
374Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
375Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
376
377jmethodID QJniObject::getMethodID(JNIEnv *env,
378 jclass clazz,
379 const char *name,
380 const char *signature,
381 bool isStatic)
382{
383 jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
384 : env->GetMethodID(clazz, name, signature);
385
386 if (QJniEnvironment::checkAndClearExceptions(env))
387 return nullptr;
388
389 return id;
390}
391
392void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
393{
394 va_list args;
395 va_start(args, id);
396 callVoidMethodV(env, id, args);
397 va_end(args);
398}
399
400void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const
401{
402 env->CallVoidMethodV(d->m_jobject, id, args);
403}
404
405jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
406 jclass clazz,
407 const QByteArray &className,
408 const char *name,
409 const char *signature,
410 bool isStatic)
411{
412 if (className.isEmpty())
413 return getMethodID(env, clazz, name, signature, isStatic);
414
417 QLatin1StringView(signature));
419
420 {
421 QReadLocker locker(cachedMethodIDLock);
422 it = cachedMethodID->constFind(key);
423 if (it != cachedMethodID->constEnd())
424 return it.value();
425 }
426
427 {
428 QWriteLocker locker(cachedMethodIDLock);
429 it = cachedMethodID->constFind(key);
430 if (it != cachedMethodID->constEnd())
431 return it.value();
432
433 jmethodID id = getMethodID(env, clazz, name, signature, isStatic);
434
435 cachedMethodID->insert(key, id);
436 return id;
437 }
438}
439
440jmethodID QJniObject::getCachedMethodID(JNIEnv *env, const char *name,
441 const char *signature, bool isStatic) const
442{
443 return QJniObject::getCachedMethodID(env, d->m_jclass, d->m_className, name, signature, isStatic);
444}
445
447Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
448Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
449
450jfieldID QJniObject::getFieldID(JNIEnv *env,
451 jclass clazz,
452 const char *name,
453 const char *signature,
454 bool isStatic)
455{
456 jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
457 : env->GetFieldID(clazz, name, signature);
458
459 if (QJniEnvironment::checkAndClearExceptions(env))
460 return nullptr;
461
462 return id;
463}
464
465jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
466 jclass clazz,
467 const QByteArray &className,
468 const char *name,
469 const char *signature,
470 bool isStatic)
471{
472 if (className.isNull())
473 return getFieldID(env, clazz, name, signature, isStatic);
474
477 QLatin1StringView(signature));
479
480 {
481 QReadLocker locker(cachedFieldIDLock);
482 it = cachedFieldID->constFind(key);
483 if (it != cachedFieldID->constEnd())
484 return it.value();
485 }
486
487 {
488 QWriteLocker locker(cachedFieldIDLock);
489 it = cachedFieldID->constFind(key);
490 if (it != cachedFieldID->constEnd())
491 return it.value();
492
493 jfieldID id = getFieldID(env, clazz, name, signature, isStatic);
494
495 cachedFieldID->insert(key, id);
496 return id;
497 }
498}
499
500jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
501 const char *name,
502 const char *signature,
503 bool isStatic) const
504{
505 return QJniObject::getCachedFieldID(env, d->m_jclass, d->m_className, name, signature, isStatic);
506}
507
508jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
509{
511 bool isCached = false;
512 jclass clazz = getCachedClass(classDotEnc, &isCached);
513
514 if (clazz || isCached)
515 return clazz;
516
517 const QLatin1StringView key(classDotEnc);
518 if (env) { // We got an env. pointer (We expect this to be the right env. and call FindClass())
519 QWriteLocker locker(cachedClassesLock);
521 // Did we lose the race?
522 if (it != cachedClasses->constEnd())
523 return it.value();
524
525 jclass fclazz = env->FindClass(className);
526 if (!QJniEnvironment::checkAndClearExceptions(env)) {
527 clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
528 env->DeleteLocalRef(fclazz);
529 }
530
531 if (clazz)
532 cachedClasses->insert(key, clazz);
533 }
534
535 if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
536 clazz = QJniObjectPrivate::loadClass(classDotEnc, QJniEnvironment().jniEnv(), true);
537
538 return clazz;
539}
540
548QJniObject::QJniObject()
549 : d(new QJniObjectPrivate())
550{
551}
552
562QJniObject::QJniObject(const char *className)
563 : d(new QJniObjectPrivate())
564{
565 QJniEnvironment env;
566 d->m_className = toBinaryEncClassName(className);
567 d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
568 d->m_own_jclass = false;
569 if (d->m_jclass) {
570 // get default constructor
571 jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", "()V");
572 if (constructorId) {
573 jobject obj = env->NewObject(d->m_jclass, constructorId);
574 if (obj) {
575 d->m_jobject = env->NewGlobalRef(obj);
576 env->DeleteLocalRef(obj);
577 }
578 }
579 }
580}
581
595QJniObject::QJniObject(const char *className, const char *signature, ...)
596 : d(new QJniObjectPrivate())
597{
598 QJniEnvironment env;
599 d->m_className = toBinaryEncClassName(className);
600 d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
601 d->m_own_jclass = false;
602 if (d->m_jclass) {
603 jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
604 if (constructorId) {
605 va_list args;
606 va_start(args, signature);
607 jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
608 va_end(args);
609 if (obj) {
610 d->m_jobject = env->NewGlobalRef(obj);
611 env->DeleteLocalRef(obj);
612 }
613 }
614 }
615}
616
633QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args)
634 : d(new QJniObjectPrivate())
635{
636 QJniEnvironment env;
637 d->m_className = toBinaryEncClassName(className);
638 d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
639 d->m_own_jclass = false;
640 if (d->m_jclass) {
641 jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
642 if (constructorId) {
643 jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
644 if (obj) {
645 d->m_jobject = env->NewGlobalRef(obj);
646 env->DeleteLocalRef(obj);
647 }
648 }
649 }
650}
651
662QJniObject::QJniObject(jclass clazz, const char *signature, ...)
663 : d(new QJniObjectPrivate())
664{
665 QJniEnvironment env;
666 if (clazz) {
667 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
668 if (d->m_jclass) {
669 jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
670 if (constructorId) {
671 va_list args;
672 va_start(args, signature);
673 jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
674 va_end(args);
675 if (obj) {
676 d->m_jobject = env->NewGlobalRef(obj);
677 env->DeleteLocalRef(obj);
678 }
679 }
680 }
681 }
682}
683
707QJniObject::QJniObject(jclass clazz)
708 : d(new QJniObjectPrivate())
709{
710 QJniEnvironment env;
711 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
712 if (d->m_jclass) {
713 // get default constructor
714 jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", "()V");
715 if (constructorId) {
716 jobject obj = env->NewObject(d->m_jclass, constructorId);
717 if (obj) {
718 d->m_jobject = env->NewGlobalRef(obj);
719 env->DeleteLocalRef(obj);
720 }
721 }
722 }
723}
724
725QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args)
726 : d(new QJniObjectPrivate())
727{
728 QJniEnvironment env;
729 if (clazz) {
730 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
731 if (d->m_jclass) {
732 jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
733 if (constructorId) {
734 jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
735 if (obj) {
736 d->m_jobject = env->NewGlobalRef(obj);
737 env->DeleteLocalRef(obj);
738 }
739 }
740 }
741 }
742}
743
756QJniObject::QJniObject(jobject object)
757 : d(new QJniObjectPrivate())
758{
759 if (!object)
760 return;
761
762 QJniEnvironment env;
763 d->m_jobject = env->NewGlobalRef(object);
764 jclass cls = env->GetObjectClass(object);
765 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
766 env->DeleteLocalRef(cls);
767}
768
789QJniObject QJniObject::getCleanJniObject(jobject object)
790{
791 if (!object)
792 return QJniObject();
793
794 QJniEnvironment env;
795 if (env.checkAndClearExceptions()) {
796 env->DeleteLocalRef(object);
797 return QJniObject();
798 }
799
800 QJniObject res(object);
801 env->DeleteLocalRef(object);
802 return res;
803}
804
810QJniObject::~QJniObject()
811{}
812
832jobject QJniObject::object() const
833{
834 return javaObject();
835}
836
850jclass QJniObject::objectClass() const
851{
852 return d->m_jclass;
853}
854
862QByteArray QJniObject::className() const
863{
864 return d->m_className;
865}
866
867QJniObject QJniObject::callObjectMethodV(const char *methodName,
868 const char *signature,
869 va_list args) const
870{
871 QJniEnvironment env;
872 jobject res = nullptr;
873 jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
874 if (id) {
875 res = env->CallObjectMethodV(d->m_jobject, id, args);
876 if (env.checkAndClearExceptions()) {
877 env->DeleteLocalRef(res);
878 res = nullptr;
879 }
880 }
881
883 env->DeleteLocalRef(res);
884 return obj;
885}
886
887QJniObject QJniObject::callStaticObjectMethodV(const char *className,
888 const char *methodName,
889 const char *signature,
890 va_list args)
891{
892 QJniEnvironment env;
893 jobject res = nullptr;
894 jclass clazz = loadClass(className, env.jniEnv());
895 if (clazz) {
896 jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
897 methodName, signature, true);
898 if (id) {
899 res = env->CallStaticObjectMethodV(clazz, id, args);
900 if (env.checkAndClearExceptions()) {
901 env->DeleteLocalRef(res);
902 res = nullptr;
903 }
904 }
905 }
906
908 env->DeleteLocalRef(res);
909 return obj;
910}
911
912QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
913 const char *methodName,
914 const char *signature,
915 va_list args)
916{
917 QJniEnvironment env;
918 jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
919 if (!id)
920 return QJniObject();
921
922 return getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
923}
924
1055QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
1056{
1057 QJniEnvironment env;
1058 jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
1059 if (id) {
1060 va_list args;
1061 va_start(args, signature);
1062 QJniObject res = getCleanJniObject(env->CallObjectMethodV(d->m_jobject, id, args));
1063 va_end(args);
1064 return res;
1065 }
1066
1067 return QJniObject();
1068}
1069
1083QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
1084 const char *signature, ...)
1085{
1086 QJniEnvironment env;
1087 jclass clazz = QJniObject::loadClass(className, env.jniEnv());
1088 if (clazz) {
1089 jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz,
1090 QJniObject::toBinaryEncClassName(className),
1091 methodName, signature, true);
1092 if (id) {
1093 va_list args;
1094 va_start(args, signature);
1095 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
1096 va_end(args);
1097 return res;
1098 }
1099 }
1100
1101 return QJniObject();
1102}
1103
1110QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName,
1111 const char *signature, ...)
1112{
1113 QJniEnvironment env;
1114 if (clazz) {
1115 jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
1116 if (id) {
1117 va_list args;
1118 va_start(args, signature);
1119 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
1120 va_end(args);
1121 return res;
1122 }
1123 }
1124
1125 return QJniObject();
1126}
1127
1143QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
1144{
1145 QJniEnvironment env;
1146 if (clazz && methodId) {
1147 va_list args;
1148 va_start(args, methodId);
1149 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args));
1150 va_end(args);
1151 return res;
1152 }
1153
1154 return QJniObject();
1155}
1156
1279QJniObject QJniObject::getStaticObjectField(const char *className,
1280 const char *fieldName,
1281 const char *signature)
1282{
1283 QJniEnvironment env;
1284 jclass clazz = QJniObject::loadClass(className, env.jniEnv());
1285 if (!clazz)
1286 return QJniObject();
1287 jfieldID id = QJniObject::getCachedFieldID(env.jniEnv(), clazz,
1288 QJniObject::toBinaryEncClassName(className),
1289 fieldName,
1290 signature, true);
1291 if (!id)
1292 return QJniObject();
1293
1294 return getCleanJniObject(env->GetStaticObjectField(clazz, id));
1295}
1296
1309QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
1310 const char *signature)
1311{
1312 QJniEnvironment env;
1313 jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
1314 if (!id)
1315 return QJniObject();
1316
1317 return getCleanJniObject(env->GetStaticObjectField(clazz, id));
1318}
1319
1354QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
1355{
1356 QJniEnvironment env;
1357 jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
1358 if (!id)
1359 return QJniObject();
1360
1361 return getCleanJniObject(env->GetObjectField(d->m_jobject, id));
1362}
1363
1409QJniObject QJniObject::fromString(const QString &string)
1410{
1411 QJniEnvironment env;
1412 return getCleanJniObject(env->NewString(reinterpret_cast<const jchar*>(string.constData()),
1413 string.length()));
1414}
1415
1430QString QJniObject::toString() const
1431{
1432 if (!isValid())
1433 return QString();
1434
1435 QJniObject string = callObjectMethod<jstring>("toString");
1436 return qt_convertJString(static_cast<jstring>(string.object()));
1437}
1438
1450bool QJniObject::isClassAvailable(const char *className)
1451{
1452 QJniEnvironment env;
1453
1454 if (!env.jniEnv())
1455 return false;
1456
1457 return loadClass(className, env.jniEnv());;
1458}
1459
1471bool QJniObject::isValid() const
1472{
1473 return d->m_jobject;
1474}
1475
1490QJniObject QJniObject::fromLocalRef(jobject lref)
1491{
1492 QJniObject obj(lref);
1493 QJniEnvironment()->DeleteLocalRef(lref);
1494 return obj;
1495}
1496
1497bool QJniObject::isSameObject(jobject obj) const
1498{
1499 return QJniEnvironment()->IsSameObject(d->m_jobject, obj);
1500}
1501
1502bool QJniObject::isSameObject(const QJniObject &other) const
1503{
1504 return isSameObject(other.d->m_jobject);
1505}
1506
1507void QJniObject::assign(jobject obj)
1508{
1509 if (isSameObject(obj))
1510 return;
1511
1512 jobject jobj = static_cast<jobject>(obj);
1514 if (obj) {
1515 QJniEnvironment env;
1516 d->m_jobject = env->NewGlobalRef(jobj);
1517 jclass objectClass = env->GetObjectClass(jobj);
1518 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
1519 env->DeleteLocalRef(objectClass);
1520 }
1521}
1522
1523jobject QJniObject::javaObject() const
1524{
1525 return d->m_jobject;
1526}
1527
\inmodule QtCore
Definition qbytearray.h:57
QByteArray & replace(qsizetype index, qsizetype len, const char *s, qsizetype alen)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:275
\inmodule QtCore
Definition qhash.h:1135
\inmodule QtCore
Definition qhash.h:818
\inmodule QtCore
static QByteArray toBinaryEncClassName(const QByteArray &className)
static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded=false)
QJniObjectPrivate()=default
QByteArray m_className
\inmodule QtCore
QString arg(Args &&...args) const
\inmodule QtCore
\inmodule QtCore
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
static QSharedPointer create(Args &&...arguments)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
\inmodule QtCore
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env)
jobject classLoader()
constexpr Initialization Uninitialized
static QString methodName(const QDBusIntrospection::Method &method)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
QHash< QString, jclass > JClassHash
static QString qt_convertJString(jstring string)
QHash< QString, jmethodID > JMethodIDHash
static QLatin1StringView keyBase()
static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached=nullptr)
QHash< QString, jfieldID > JFieldIDHash
GLuint64 key
GLenum GLuint id
[7]
GLuint name
GLhandleARB obj
[2]
GLuint res
const char className[16]
[1]
Definition qwizard.cpp:100
QSharedPointer< T > other(t)
[5]
QJSValueList args