Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:significant reason:default
4
5#include "qjniobject.h"
6
8
9#include <QtCore/qbytearray.h>
10#include <QtCore/qhash.h>
11#include <QtCore/qreadwritelock.h>
12#include <QtCore/qloggingcategory.h>
13
14
16
17Q_LOGGING_CATEGORY(lcJniThreadCheck, "qt.core.jni.threadcheck")
18
19using namespace Qt::StringLiterals;
20
21/*!
22 \class QJniObject
23 \inmodule QtCore
24 \since 6.1
25 \brief A convenience wrapper around the Java Native Interface (JNI).
26
27 The QJniObject class wraps a reference to a Java object, ensuring it isn't
28 garbage-collected and providing access to most \c JNIEnv method calls
29 (member, static) and fields (setter, getter). It eliminates much
30 boiler-plate that would normally be needed, with direct JNI access, for
31 every operation. Exceptions thrown by called Java methods are cleared by
32 default, but can since Qt 6.11 also be handled by the caller.
33
34 \note This API has been designed and tested for use with Android.
35 It has not been tested for other platforms.
36
37 \sa QJniEnvironment
38
39 \section1 Method Signatures
40
41 QJniObject provides convenience functions that will use the correct signature based on the
42 provided or deduced template arguments.
43
44 \code
45 jint x = QJniObject::callMethod<jint>("getSize");
46 QJniObject::callMethod<void>("touch");
47 jint ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
48 \endcode
49
50 These functions are variadic templates, and the compiler will deduce the
51 signature from the actual argument types. Only the return type needs to be
52 provided explicitly. QJniObject can deduce the signature string for
53 functions that take \l {JNI types}, and for types that have been declared
54 with the QtJniTypes type mapping.
55
56 \code
57 // Java class
58 package org.qtproject.qt;
59 class TestClass
60 {
61 static TestClass create() { ... }
62 static String fromNumber(int x) { ... }
63 static String[] stringArray(String s1, String s2) { ... }
64 }
65 \endcode
66
67 \code
68 // C++ code
69 Q_DECLARE_JNI_CLASS(TestClass, "org/qtproject/qt/TestClass")
70
71 // ...
72 using namespace QtJniTypes;
73 TestClass testClass = TestClass::callStaticMethod<TestClass>("create");
74 \endcode
75
76 This allows working with arbitrary Java and Android types in C++ code, without having to
77 create JNI signature strings explicitly.
78
79 \section2 Explicit JNI Signatures
80
81 It is possible to supply the signature yourself. In that case, it is important
82 that the signature matches the function you want to call.
83
84 \list
85 \li Class names need to be fully-qualified, for example: \c "java/lang/String".
86 \li Method signatures are written as \c "(ArgumentsTypes)ReturnType", see \l {JNI Types}.
87 \li All object types are returned as a QJniObject.
88 \endlist
89
90 The example below demonstrates how to call different static functions:
91
92 The signature structure is \c "(ArgumentsTypes)ReturnType". Array types in the signature
93 must have the \c {[} prefix, and the fully-qualified \c Object type names must have the
94 \c L prefix and the \c ; suffix. The signature for the \c create function is
95 \c {"()Lorg/qtproject/qt/TestClass;}. The signatures for the second and third functions
96 are \c {"(I)Ljava/lang/String;"} and
97 \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}, respectively.
98
99 We can call the \c create() function like this:
100
101 \code
102 // C++ code
103 QJniObject testClass = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
104 "create",
105 "()Lorg/qtproject/qt/TestClass;");
106 \endcode
107
108 For the second and third function we can rely on QJniObject's template methods to create
109 the implicit signature string, but we can also pass the signature string explicitly:
110
111 \code
112 // C++ code
113 QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
114 "fromNumber",
115 "(I)Ljava/lang/String;", 10);
116 \endcode
117
118 For the implicit signature creation to work we need to specify the return type explicitly:
119
120 \code
121 // C++ code
122 QJniObject string1 = QJniObject::fromString("String1");
123 QJniObject string2 = QJniObject::fromString("String2");
124 QJniObject stringArray = QJniObject::callStaticObjectMethod<jobjectArray>(
125 "org/qtproject/qt/TestClass",
126 "stringArray",
127 string1.object<jstring>(),
128 string2.object<jstring>());
129 \endcode
130
131 Note that while the first template parameter specifies the return type of the Java
132 function, the method will still return a QJniObject.
133
134 \section1 Handling Java Exceptions
135
136 After calling Java functions that might throw exceptions, it is important
137 to check for, handle and clear out any exception before continuing. All
138 QJniObject functions can handle exceptions internally by reporting and
139 clearing them. This includes JNI exceptions, for instance when trying to
140 call a method that doesn't exist, or with bad parameters; and exceptions
141 are thrown by the method as a way of reporting errors or returning failure
142 information.
143
144 From Qt 6.11 on, client code can opt in to handle exceptions explicitly in
145 each call. To do so, use \c{std::expected} from C++ 23 as the return type,
146 with the value type as the expected, and \c{jthrowable} as the error type.
147 For instance, trying to read a setting value via the
148 \c{android.provider.Settings.Secure} type might throw an exception if the
149 setting does not exist.
150
151 \code
152 Q_DECLARE_JNI_CLASS(SettingsSecure, "android/provider/Settings$Secure")
153 using namespace QtJniTypes;
154
155 QString enabledInputMethods()
156 {
157 ContentResolver resolver;
158 SettingsSecure settings;
159
160 auto defaultInputMethods = settings.callMethod<std::expected<QString, jthrowable>>(
161 "getString", resolver, u"enabled_input_methods"_s
162 );
163 if (defaultInputMethods)
164 return defaultInputMethods.value();
165 QStringList stackTrace = QJniEnvironment::stackTrace(defaultInputMethods.error());
166 }
167 \endcode
168
169 You can use any other type that behaves like \c{std::expected}, so handling
170 exceptions explicitly is possible without using C++23. The only
171 requirements are that the type declares three nested types \c{value_type},
172 \c{error_type}, and \c{unexpected_type}, can be constructed from the value
173 type, and from its \c{unexpected_type} holding a \c{jthrowable}.
174
175 \note The user must handle exceptions manually when doing JNI calls using \c JNIEnv directly.
176 It is unsafe to make other JNI calls when exceptions are pending. For more information, see
177 QJniEnvironment::checkAndClearExceptions().
178
179 \section1 Java Native Methods
180
181 Java native methods makes it possible to call native code from Java, this is done by creating a
182 function declaration in Java and prefixing it with the \c native keyword.
183 Before a native function can be called from Java, you need to map the Java native function to a
184 native function in your code. Mapping functions can be done by calling
185 QJniEnvironment::registerNativeMethods().
186
187 The example below demonstrates how this could be done.
188
189 Java implementation:
190 \snippet jni/src_qjniobject.cpp Java native methods
191
192 C++ Implementation:
193 \snippet jni/src_qjniobject.cpp C++ native methods
194
195 \section1 The Lifetime of a Java Object
196
197 Most \l{Object types}{objects} received from Java will be local references
198 and will only stay valid until you return from the native method. After that,
199 the object becomes eligible for garbage collection. If your code creates
200 many local references in a loop you should delete them manually with each
201 iteration, otherwise you might run out of memory. For more information, see
202 \l {JNI Design Overview: Global and Local References}. Local references
203 created outside a native method scope must be deleted manually, since
204 the garbage collector will not free them automatically because we are using
205 \l {Java: AttachCurrentThread}{AttachCurrentThread}. For more information, see
206 \l {JNI tips: Local and global references}.
207
208 If you want to keep a Java object alive you need to either create a new global
209 reference to the object and release it when you are done, or construct a new
210 QJniObject and let it manage the lifetime of the Java object.
211
212 \sa object()
213
214 \note The QJniObject only manages its own references, if you construct a QJniObject from a
215 global or local reference that reference will not be released by the QJniObject.
216
217 \section1 JNI Types
218
219 \section2 Object Types
220 \table
221 \header
222 \li Type
223 \li Signature
224 \row
225 \li jobject
226 \li Ljava/lang/Object;
227 \row
228 \li jclass
229 \li Ljava/lang/Class;
230 \row
231 \li jstring
232 \li Ljava/lang/String;
233 \row
234 \li jthrowable
235 \li Ljava/lang/Throwable;
236 \row
237 \li jobjectArray
238 \li [Ljava/lang/Object;
239 \row
240 \li jarray
241 \li [\e<type>
242 \row
243 \li jbooleanArray
244 \li [Z
245 \row
246 \li jbyteArray
247 \li [B
248 \row
249 \li jcharArray
250 \li [C
251 \row
252 \li jshortArray
253 \li [S
254 \row
255 \li jintArray
256 \li [I
257 \row
258 \li jlongArray
259 \li [J
260 \row
261 \li jfloatArray
262 \li [F
263 \row
264 \li jdoubleArray
265 \li [D
266 \endtable
267
268 \section2 Primitive Types
269 \table
270 \header
271 \li Type
272 \li Signature
273 \row
274 \li jboolean
275 \li Z
276 \row
277 \li jbyte
278 \li B
279 \row
280 \li jchar
281 \li C
282 \row
283 \li jshort
284 \li S
285 \row
286 \li jint
287 \li I
288 \row
289 \li jlong
290 \li J
291 \row
292 \li jfloat
293 \li F
294 \row
295 \li jdouble
296 \li D
297 \endtable
298
299 \section2 Other
300 \table
301 \header
302 \li Type
303 \li Signature
304 \row
305 \li void
306 \li V
307 \row
308 \li \e{Custom type}
309 \li L\e<fully-qualified-name>;
310 \endtable
311
312 For more information about JNI, see \l {Java Native Interface Specification}.
313*/
314
315/*!
316 \fn bool operator==(const QJniObject &o1, const QJniObject &o2)
317
318 \relates QJniObject
319
320 Returns true if both objects, \a o1 and \a o2, are referencing the same Java object, or if both
321 are NULL. In any other cases false will be returned.
322*/
323
324/*!
325 \fn bool operator!=(const QJniObject &o1, const QJniObject &o2)
326 \relates QJniObject
327
328 Returns true if \a o1 holds a reference to a different object than \a o2.
329*/
330
332{
333public:
335 {
336 }
338 JNIEnv *env = QJniEnvironment::getJniEnv();
339 if (m_jobject)
340 env->DeleteGlobalRef(m_jobject);
341 if (m_jclass && m_own_jclass)
342 env->DeleteGlobalRef(m_jclass);
343 }
344
345 template <typename ...Args>
346 void construct(const char *signature = nullptr, Args &&...args)
347 {
348 if (m_jclass) {
349 JNIEnv *env = QJniEnvironment::getJniEnv();
350 // get default constructor
351 jmethodID constructorId = QJniObject::getCachedMethodID(env, m_jclass, m_className, "<init>",
352 signature ? signature : "()V");
353 if (constructorId) {
354 jobject obj = nullptr;
355 if constexpr (sizeof...(Args) == 0)
356 obj = env->NewObject(m_jclass, constructorId);
357 else
358 obj = env->NewObjectV(m_jclass, constructorId, std::forward<Args>(args)...);
359 if (obj) {
360 m_jobject = env->NewGlobalRef(obj);
361 env->DeleteLocalRef(obj);
362 }
363 }
364 }
365 }
366
367 bool isJString(JNIEnv *env) const;
368
371 jclass m_jclass = nullptr;
372 bool m_own_jclass = true;
373 enum class IsJString : quint8 {
377 };
379};
380
381template <typename ...Args>
382static inline QByteArray cacheKey(Args &&...args)
383{
384 return (QByteArrayView(":") + ... + QByteArrayView(args));
385}
386
391Q_GLOBAL_STATIC(JClassHash, cachedClasses)
392Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
393
394static jclass getCachedClass(const QByteArray &className)
395{
396 QReadLocker locker(cachedClassesLock);
397 const auto &it = cachedClasses->constFind(className);
398 return it != cachedClasses->constEnd() ? it.value() : nullptr;
399}
400
401
402bool QJniObjectPrivate::isJString(JNIEnv *env) const
403{
404 Q_ASSERT(m_jobject);
405 if (m_is_jstring != IsJString::Unknown)
406 return m_is_jstring == IsJString::Yes;
407
408 static constexpr auto stringSignature = QtJniTypes::Traits<jstring>::className();
409 if (!m_className.isEmpty()) {
410 m_is_jstring = m_className == stringSignature
411 ? IsJString::Yes
412 : IsJString::No;
413 } else {
414 const jclass strClass = getCachedClass(stringSignature.data());
415 const bool isStringInstance = strClass
416 ? env->IsInstanceOf(m_jobject, strClass)
417 : false;
418 m_is_jstring = isStringInstance ? IsJString::Yes : IsJString::No;
419 }
420
421 return m_is_jstring == IsJString::Yes;
422}
423
424/*!
425 \internal
426
427 Get a JNI object from a jobject variant and do the necessary
428 exception clearing and delete the local reference before returning.
429 The JNI object can be null if there was an exception.
430*/
431static QJniObject getCleanJniObject(jobject object, JNIEnv *env)
432{
433 if (!object || env->ExceptionCheck())
434 return QJniObject();
435
436 return QJniObject::fromLocalRef(object);
437}
438
439namespace {
440
441jclass loadClassHelper(const QByteArray &className, JNIEnv *env)
442{
443 Q_ASSERT(env);
444 QByteArray classNameArray(className);
445#ifdef QT_DEBUG
446 if (classNameArray.contains('.')) {
447 qWarning("QtAndroidPrivate::findClass: className '%s' should use slash separators!",
448 className.constData());
449 }
450#endif
451 classNameArray.replace('.', '/');
452 jclass clazz = getCachedClass(classNameArray);
453 if (clazz)
454 return clazz;
455
456 QWriteLocker locker(cachedClassesLock);
457 // Check again; another thread might have added the class to the cache after
458 // our call to getCachedClass and before we acquired the lock.
459 const auto &it = cachedClasses->constFind(classNameArray);
460 if (it != cachedClasses->constEnd())
461 return it.value();
462
463 // JNIEnv::FindClass wants "a fully-qualified class name or an array type signature"
464 // which is a slash-separated class name.
465 jclass localClazz = env->FindClass(classNameArray.constData());
466 if (localClazz) {
467 clazz = static_cast<jclass>(env->NewGlobalRef(localClazz));
468 env->DeleteLocalRef(localClazz);
469 } else {
470 // Clear the exception silently; we are going to try the ClassLoader next,
471 // so no need for warning unless that fails as well.
472 env->ExceptionClear();
473 }
474
475 if (!clazz) {
476 // Wrong class loader, try our own
477 jobject classLoader = QtAndroidPrivate::classLoader();
478 if (!classLoader)
479 return nullptr;
480
481 if (!cachedClasses->loadClassMethod) {
482 jclass classLoaderClass = env->GetObjectClass(classLoader);
483 if (!classLoaderClass)
484 return nullptr;
485 cachedClasses->loadClassMethod =
486 env->GetMethodID(classLoaderClass,
487 "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
488 env->DeleteLocalRef(classLoaderClass);
489 if (!cachedClasses->loadClassMethod) {
490 qCritical("Couldn't find the 'loadClass' method in the Qt class loader");
491 return nullptr;
492 }
493 }
494
495 // ClassLoader::loadClass on the other hand wants the binary name of the class,
496 // e.g. dot-separated. In testing it works also with /, but better to stick to
497 // the specification.
498 const QString binaryClassName = QString::fromLatin1(className).replace(u'/', u'.');
499 jstring classNameObject = env->NewString(binaryClassName.utf16(), binaryClassName.length());
500 jobject classObject = env->CallObjectMethod(classLoader,
501 cachedClasses->loadClassMethod,
502 classNameObject);
503 env->DeleteLocalRef(classNameObject);
504
505 if (classObject && !env->ExceptionCheck()) {
506 clazz = static_cast<jclass>(env->NewGlobalRef(classObject));
507 env->DeleteLocalRef(classObject);
508 }
509 // Clearing the exception is the caller's responsibility (see
510 // QtAndroidPrivate::findClass()) and QJniObject::loadClass{KeepExceptions}
511 }
512
513 if (clazz)
514 cachedClasses->insert(classNameArray, clazz);
515
516 return clazz;
517}
518
519} // unnamed namespace
520
521/*!
522 \internal
523 \a className must be slash-encoded
524*/
525jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
526{
527 jclass clazz = loadClassHelper(className, env);
528 if (!clazz)
529 QJniEnvironment::checkAndClearExceptions(env);
530 return clazz;
531}
532
533jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env)
534{
535 return QtAndroidPrivate::findClass(className, env);
536}
537
538jclass QJniObject::loadClassKeepExceptions(const QByteArray &className, JNIEnv *env)
539{
540 return loadClassHelper(className, env);
541}
542
543
544
545typedef QHash<QByteArray, jmethodID> JMethodIDHash;
546Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
547Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
548
549jmethodID QJniObject::getMethodID(JNIEnv *env,
550 jclass clazz,
551 const char *name,
552 const char *signature,
553 bool isStatic)
554{
555 jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
556 : env->GetMethodID(clazz, name, signature);
557
558 return id;
559}
560
561void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
562{
563 va_list args;
564 va_start(args, id);
565 env->CallVoidMethodV(d->m_jobject, id, args);
566 va_end(args);
567}
568
569jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
570 jclass clazz,
571 const QByteArray &className,
572 const char *name,
573 const char *signature,
574 bool isStatic)
575{
576 if (className.isEmpty())
577 return getMethodID(env, clazz, name, signature, isStatic);
578
579 const QByteArray key = cacheKey(className, name, signature);
580 QHash<QByteArray, jmethodID>::const_iterator it;
581
582 {
583 QReadLocker locker(cachedMethodIDLock);
584 it = cachedMethodID->constFind(key);
585 if (it != cachedMethodID->constEnd())
586 return it.value();
587 }
588
589 {
590 QWriteLocker locker(cachedMethodIDLock);
591 it = cachedMethodID->constFind(key);
592 if (it != cachedMethodID->constEnd())
593 return it.value();
594
595 jmethodID id = getMethodID(env, clazz, name, signature, isStatic);
596
597 if (id)
598 cachedMethodID->insert(key, id);
599 return id;
600 }
601}
602
603jmethodID QJniObject::getCachedMethodID(JNIEnv *env, const char *name,
604 const char *signature, bool isStatic) const
605{
606 return QJniObject::getCachedMethodID(env, d->m_jclass, d->m_className, name, signature, isStatic);
607}
608
609typedef QHash<QByteArray, jfieldID> JFieldIDHash;
610Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
611Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
612
613jfieldID QJniObject::getFieldID(JNIEnv *env,
614 jclass clazz,
615 const char *name,
616 const char *signature,
617 bool isStatic)
618{
619 jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
620 : env->GetFieldID(clazz, name, signature);
621
622 return id;
623}
624
625jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
626 jclass clazz,
627 const QByteArray &className,
628 const char *name,
629 const char *signature,
630 bool isStatic)
631{
632 if (className.isNull())
633 return getFieldID(env, clazz, name, signature, isStatic);
634
635 const QByteArray key = cacheKey(className, name, signature);
636 QHash<QByteArray, jfieldID>::const_iterator it;
637
638 {
639 QReadLocker locker(cachedFieldIDLock);
640 it = cachedFieldID->constFind(key);
641 if (it != cachedFieldID->constEnd())
642 return it.value();
643 }
644
645 {
646 QWriteLocker locker(cachedFieldIDLock);
647 it = cachedFieldID->constFind(key);
648 if (it != cachedFieldID->constEnd())
649 return it.value();
650
651 jfieldID id = getFieldID(env, clazz, name, signature, isStatic);
652
653 if (id)
654 cachedFieldID->insert(key, id);
655 return id;
656 }
657}
658
659jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
660 const char *name,
661 const char *signature,
662 bool isStatic) const
663{
664 return QJniObject::getCachedFieldID(env, d->m_jclass, d->m_className, name, signature, isStatic);
665}
666
667/*!
668 \fn QJniObject::QJniObject()
669
670 Constructs an invalid JNI object.
671
672 \sa isValid()
673*/
674QJniObject::QJniObject()
675 : d(new QJniObjectPrivate())
676{
677}
678
679/*!
680 \fn QJniObject::QJniObject(const char *className)
681
682 Constructs a new JNI object by calling the default constructor of \a className.
683
684 \code
685 QJniObject myJavaString("java/lang/String");
686 \endcode
687*/
688QJniObject::QJniObject(const char *className)
689 : d(new QJniObjectPrivate())
690{
691 d->m_className = className;
692 d->m_jclass = loadClass(d->m_className, jniEnv());
693 d->m_own_jclass = false;
694
695 d->construct();
696}
697
698/*!
699 \fn QJniObject::QJniObject(const char *className, const char *signature, ...)
700
701 Constructs a new JNI object by calling the constructor of \a className with
702 \a signature specifying the types of any subsequent arguments.
703
704 \code
705 QJniEnvironment env;
706 char* str = "Hello";
707 jstring myJStringArg = env->NewStringUTF(str);
708 QJniObject myNewJavaString("java/lang/String", "(Ljava/lang/String;)V", myJStringArg);
709 \endcode
710*/
711QJniObject::QJniObject(const char *className, const char *signature, ...)
712 : d(new QJniObjectPrivate())
713{
714 d->m_className = className;
715 d->m_jclass = loadClass(d->m_className, jniEnv());
716 d->m_own_jclass = false;
717
718 va_list args;
719 va_start(args, signature);
720 d->construct(signature, args);
721 va_end(args);
722}
723
724/*!
725 \fn template<typename ...Args> QJniObject::QJniObject(const char *className, Args &&...args)
726 \since 6.4
727
728 Constructs a new JNI object by calling the constructor of \a className with
729 the arguments \a args. This constructor is only available if all \a args are
730 known \l {JNI Types}.
731
732 \code
733 QJniEnvironment env;
734 char* str = "Hello";
735 jstring myJStringArg = env->NewStringUTF(str);
736 QJniObject myNewJavaString("java/lang/String", myJStringArg);
737 \endcode
738*/
739
740/*!
741 Constructs a new JNI object from \a clazz by calling the constructor with
742 \a signature specifying the types of any subsequent arguments.
743
744 \code
745 QJniEnvironment env;
746 jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
747 QJniObject(myClazz, "(I)V", 3);
748 \endcode
749*/
750QJniObject::QJniObject(jclass clazz, const char *signature, ...)
751 : d(new QJniObjectPrivate())
752{
753 if (clazz) {
754 d->m_jclass = static_cast<jclass>(jniEnv()->NewGlobalRef(clazz));
755 va_list args;
756 va_start(args, signature);
757 d->construct(signature, args);
758 va_end(args);
759 }
760}
761
762/*!
763 \fn template<typename ...Args> QJniObject::QJniObject(jclass clazz, Args &&...args)
764 \since 6.4
765
766 Constructs a new JNI object from \a clazz by calling the constructor with
767 the arguments \a args. This constructor is only available if all \a args are
768 known \l {JNI Types}.
769
770 \code
771 QJniEnvironment env;
772 jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
773 QJniObject(myClazz, 3);
774 \endcode
775*/
776
777/*!
778 Constructs a new JNI object by calling the default constructor of \a clazz.
779
780 \note The QJniObject will create a new reference to the class \a clazz
781 and releases it again when it is destroyed. References to the class created
782 outside the QJniObject need to be managed by the caller.
783*/
784
785QJniObject::QJniObject(jclass clazz)
786 : QJniObject(clazz, "()V")
787{
788}
789
790/*!
791 Constructs a new JNI object around the Java object \a object.
792
793 \note The QJniObject will hold a reference to the Java object \a object
794 and release it when destroyed. Any references to the Java object \a object
795 outside QJniObject needs to be managed by the caller. In most cases you
796 should never call this function with a local reference unless you intend
797 to manage the local reference yourself. See QJniObject::fromLocalRef()
798 for converting a local reference to a QJniObject.
799
800 \sa fromLocalRef()
801*/
802QJniObject::QJniObject(jobject object)
803 : d(new QJniObjectPrivate())
804{
805 if (!object)
806 return;
807
808 JNIEnv *env = jniEnv();
809 d->m_jobject = env->NewGlobalRef(object);
810 jclass cls = env->GetObjectClass(object);
811 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
812 env->DeleteLocalRef(cls);
813}
814
815/*!
816 \fn template<typename Class, typename ...Args> static inline QJniObject QJniObject::construct(Args &&...args)
817 \since 6.4
818
819 Constructs an instance of the Java class that is the equivalent of \c Class and
820 returns a QJniObject containing the JNI object. The arguments in \a args are
821 passed to the Java constructor.
822
823 \code
824 QJniObject javaString = QJniObject::construct<jstring>();
825 \endcode
826
827 This function is only available if all \a args are known \l {JNI Types}.
828*/
829
830/*!
831 \fn QJniObject::~QJniObject()
832
833 Destroys the JNI object and releases any references held by the JNI object.
834*/
835QJniObject::~QJniObject()
836{}
837
838/*!
839 \fn void QJniObject::swap(QJniObject &other)
840 \since 6.8
841 \memberswap{object}
842*/
843
844
845namespace {
846QByteArray getClassNameHelper(JNIEnv *env, const QJniObjectPrivate *d)
847{
848 if (env->PushLocalFrame(3) != JNI_OK) // JVM out of memory
849 return QByteArray();
850
851 jmethodID mid = env->GetMethodID(d->m_jclass, "getClass", "()Ljava/lang/Class;");
852 jobject classObject = env->CallObjectMethod(d->m_jobject, mid);
853 jclass classObjectClass = env->GetObjectClass(classObject);
854 mid = env->GetMethodID(classObjectClass, "getName", "()Ljava/lang/String;");
855 jstring stringObject = static_cast<jstring>(env->CallObjectMethod(classObject, mid));
856 const jsize length = env->GetStringUTFLength(stringObject);
857 const char* nameString = env->GetStringUTFChars(stringObject, NULL);
858 const QByteArray result = QByteArray::fromRawData(nameString, length).replace('.', '/');
859 env->ReleaseStringUTFChars(stringObject, nameString);
860 env->PopLocalFrame(nullptr);
861 return result;
862}
863}
864
865/*! \internal
866
867 Returns the JNIEnv of the calling thread.
868*/
869JNIEnv *QJniObject::jniEnv() const noexcept
870{
871 return QJniEnvironment::getJniEnv();
872}
873
874/*!
875 \fn jobject QJniObject::object() const
876 \fn template <typename T> T QJniObject::object() const
877
878 Returns the object held by the QJniObject either as jobject or as type T.
879 T can be one of \l {Object Types}{JNI Object Types}.
880
881 \code
882 QJniObject string = QJniObject::fromString("Hello, JNI");
883 jstring jstring = string.object<jstring>();
884 \endcode
885
886 \note The returned object is still kept alive by this QJniObject. To keep the
887 object alive beyond the lifetime of this QJniObject, for example to record it
888 for later use, the easiest approach is to store it in another QJniObject with
889 a suitable lifetime. Alternatively, you may create a new global reference to the
890 object and store it, taking care to free it when you are done with it.
891
892 \snippet jni/src_qjniobject.cpp QJniObject scope
893*/
894jobject QJniObject::object() const
895{
896 return javaObject();
897}
898
899/*!
900 \fn jclass QJniObject::objectClass() const
901
902 Returns the class object held by the QJniObject as a \c jclass.
903
904 \note The returned object is still kept alive by this QJniObject. To keep the
905 object alive beyond the lifetime of this QJniObject, for example to record it
906 for later use, the easiest approach is to store it in another QJniObject with
907 a suitable lifetime. Alternatively, you may create a new global reference to the
908 object and store it, taking care to free it when you are done with it.
909
910 \since 6.2
911*/
912jclass QJniObject::objectClass() const
913{
914 return d->m_jclass;
915}
916
917/*!
918 \fn QByteArray QJniObject::className() const
919
920 Returns the name of the class object held by the QJniObject as a \c QByteArray.
921
922 \since 6.2
923*/
924QByteArray QJniObject::className() const
925{
926 if (d->m_className.isEmpty() && d->m_jclass && d->m_jobject) {
927 JNIEnv *env = jniEnv();
928 d->m_className = getClassNameHelper(env, d.get());
929 }
930 return d->m_className;
931}
932
933/*!
934 \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, const char *signature, Args &&...args) const
935 \since 6.4
936
937 Calls the object's method \a methodName with \a signature specifying the types of any
938 subsequent arguments \a args, and returns the value (unless Ret is \c void). If Ret
939 is a jobject type, then the returned value will be a QJniObject.
940
941 \code
942 QJniObject myJavaString("org/qtproject/qt/TestClass");
943 jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051);
944 \endcode
945*/
946
947/*!
948 \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, Args &&...args) const
949 \since 6.4
950
951 Calls the method \a methodName with arguments \a args and returns the value
952 (unless Ret is \c void). If Ret is a jobject type, then the returned value
953 will be a QJniObject.
954
955 \code
956 QJniObject myJavaString("org/qtproject/qt/TestClass");
957 jint size = myJavaString.callMethod<jint>("length");
958 \endcode
959
960 The method signature is deduced at compile time from Ret and the types
961 of \a args. Ret can be a \c{std::expected}-compatible type that returns
962 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
963 called method.
964*/
965
966/*!
967 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
968 \since 6.4
969
970 Calls the static method \a methodName from class \a className with \a signature
971 specifying the types of any subsequent arguments \a args. Returns the result of
972 the method (unless Ret is \c void). If Ret is a jobject type, then the
973 returned value will be a QJniObject.
974
975 \code
976 jint a = 2;
977 jint b = 4;
978 jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
979 \endcode
980*/
981
982/*!
983 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, Args &&...args)
984 \since 6.4
985
986 Calls the static method \a methodName on class \a className with arguments \a args,
987 and returns the value of type Ret (unless Ret is \c void). If Ret
988 is a jobject type, then the returned value will be a QJniObject.
989
990 \code
991 jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
992 \endcode
993
994 The method signature is deduced at compile time from Ret and the types
995 of \a args. Ret can be a \c{std::expected}-compatible type that returns
996 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
997 called method.
998*/
999
1000/*!
1001 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
1002
1003 Calls the static method \a methodName from \a clazz with \a signature
1004 specifying the types of any subsequent arguments. Returns the result of
1005 the method (unless Ret is \c void). If Ret is a jobject type, then the
1006 returned value will be a QJniObject.
1007
1008 \code
1009 QJniEnvironment env;
1010 jclass javaMathClass = env.findClass("java/lang/Math");
1011 jint a = 2;
1012 jint b = 4;
1013 jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b);
1014 \endcode
1015*/
1016
1017/*!
1018 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
1019 \since 6.4
1020
1021 Calls the static method identified by \a methodId from the class \a clazz
1022 with any subsequent arguments, and returns the value of type Ret (unless
1023 Ret is \c void). If Ret is a jobject type, then the returned value will
1024 be a QJniObject.
1025
1026 Useful when \a clazz and \a methodId are already cached from previous operations.
1027
1028 \code
1029 QJniEnvironment env;
1030 jclass javaMathClass = env.findClass("java/lang/Math");
1031 jmethodID methodId = env.findStaticMethod(javaMathClass, "max", "(II)I");
1032 if (methodId != 0) {
1033 jint a = 2;
1034 jint b = 4;
1035 jint max = QJniObject::callStaticMethod<jint>(javaMathClass, methodId, a, b);
1036 }
1037 \endcode
1038*/
1039
1040/*!
1041 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
1042 \since 6.4
1043
1044 Calls the static method \a methodName on \a clazz and returns the value of type Ret
1045 (unless Ret is \c void). If Ret is a jobject type, then the returned value will
1046 be a QJniObject.
1047
1048 \code
1049 QJniEnvironment env;
1050 jclass javaMathClass = env.findClass("java/lang/Math");
1051 jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
1052 \endcode
1053
1054 The method signature is deduced at compile time from Ret and the types
1055 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1056 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1057 called method.
1058*/
1059
1060/*!
1061 \fn template <typename Klass, typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *methodName, Args &&...args)
1062 \since 6.7
1063
1064 Calls the static method \a methodName on the class \c Klass and returns the value of type
1065 Ret (unless Ret is \c void). If Ret is a jobject type, then the returned value will
1066 be a QJniObject.
1067
1068 The method signature is deduced at compile time from Ret and the types
1069 of \a args. \c Klass needs to be a C++ type with a registered type mapping
1070 to a Java type. Ret can be a \c{std::expected}-compatible type that
1071 returns a value, or \l{Handling Java Exceptions}{any Java exception thrown}
1072 by the called method.
1073
1074*/
1075
1076/*!
1077 \fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
1078
1079 Calls the Java object's method \a methodName with \a signature specifying
1080 the types of any subsequent arguments.
1081
1082 \code
1083 QJniObject myJavaString = QJniObject::fromString("Hello, Java");
1084 QJniObject mySubstring = myJavaString.callObjectMethod("substring",
1085 "(II)Ljava/lang/String;", 7, 11);
1086 \endcode
1087*/
1088QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
1089{
1090 JNIEnv *env = jniEnv();
1091 jmethodID id = getCachedMethodID(env, methodName, signature);
1092 va_list args;
1093 va_start(args, signature);
1094 // can't go back from variadic arguments to variadic templates
1095 jobject object = id ? jniEnv()->CallObjectMethodV(javaObject(), id, args) : nullptr;
1096 QJniObject res = getCleanJniObject(object, env);
1097 va_end(args);
1098
1099 QJniEnvironment::checkAndClearExceptions(env);
1100 return res;
1101}
1102
1103/*!
1104 \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...)
1105
1106 Calls the static method \a methodName from the class \a className with \a signature
1107 specifying the types of any subsequent arguments.
1108
1109 \code
1110 QJniObject thread = QJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread",
1111 "()Ljava/lang/Thread;");
1112 QJniObject string = QJniObject::callStaticObjectMethod("java/lang/String", "valueOf",
1113 "(I)Ljava/lang/String;", 10);
1114 \endcode
1115*/
1116QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
1117 const char *signature, ...)
1118{
1119 JNIEnv *env = QJniEnvironment::getJniEnv();
1120 jclass clazz = QJniObject::loadClass(className, env);
1121 if (clazz) {
1122 jmethodID id = QJniObject::getCachedMethodID(env, clazz,
1123 className,
1124 methodName, signature, true);
1125 if (id) {
1126 va_list args;
1127 va_start(args, signature);
1128 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env);
1129 va_end(args);
1130 return res;
1131 }
1132 }
1133
1134 return QJniObject();
1135}
1136
1137/*!
1138 \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...)
1139
1140 Calls the static method \a methodName from class \a clazz with \a signature
1141 specifying the types of any subsequent arguments.
1142*/
1143QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName,
1144 const char *signature, ...)
1145{
1146 if (clazz) {
1147 QJniEnvironment env;
1148 jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
1149 if (id) {
1150 va_list args;
1151 va_start(args, signature);
1152 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env.jniEnv());
1153 va_end(args);
1154 return res;
1155 }
1156 }
1157
1158 return QJniObject();
1159}
1160
1161/*!
1162 \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
1163
1164 Calls the static method identified by \a methodId from the class \a clazz
1165 with any subsequent arguments. Useful when \a clazz and \a methodId are
1166 already cached from previous operations.
1167
1168 \code
1169 QJniEnvironment env;
1170 jclass clazz = env.findClass("java/lang/String");
1171 jmethodID methodId = env.findStaticMethod(clazz, "valueOf", "(I)Ljava/lang/String;");
1172 if (methodId != 0)
1173 QJniObject str = QJniObject::callStaticObjectMethod(clazz, methodId, 10);
1174 \endcode
1175*/
1176QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
1177{
1178 if (clazz && methodId) {
1179 JNIEnv *env = QJniEnvironment::getJniEnv();
1180 va_list args;
1181 va_start(args, methodId);
1182 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args), env);
1183 va_end(args);
1184 return res;
1185 }
1186
1187 return QJniObject();
1188}
1189
1190/*!
1191 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callObjectMethod(const char *methodName, Args &&...args) const
1192 \since 6.4
1193
1194 Calls the Java objects method \a methodName with arguments \a args and returns a
1195 new QJniObject for the returned Java object.
1196
1197 \code
1198 QJniObject myJavaString = QJniObject::fromString("Hello, Java");
1199 QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
1200 \endcode
1201
1202 The method signature is deduced at compile time from Ret and the types
1203 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1204 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1205 called method.
1206*/
1207
1208/*!
1209 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
1210 \since 6.4
1211
1212 Calls the static method with \a methodName on the class \a className, passing
1213 arguments \a args, and returns a new QJniObject for the returned Java object.
1214
1215 \code
1216 QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
1217 \endcode
1218
1219 The method signature is deduced at compile time from Ret and the types
1220 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1221 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1222 called method.
1223*/
1224
1225/*!
1226 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
1227 \since 6.4
1228
1229 Calls the static method with \a methodName on \a clazz, passing arguments \a args,
1230 and returns a new QJniObject for the returned Java object.
1231*/
1232
1233/*!
1234 \fn template <typename T, std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true> QJniObject &QJniObject::operator=(T object)
1235
1236 Replace the current object with \a object. The old Java object will be released.
1237*/
1238
1239/*!
1240 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, Type value);
1241
1242 Sets the static field \a fieldName on the class \a className to \a value
1243 using the setter with \a signature.
1244
1245*/
1246
1247/*!
1248 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, Type value);
1249
1250 Sets the static field \a fieldName on the class \a clazz to \a value using
1251 the setter with \a signature.
1252*/
1253
1254/*!
1255 \fn template<typename T> T QJniObject::getField(const char *fieldName) const
1256
1257 Retrieves the value of the field \a fieldName.
1258
1259 \code
1260 QJniObject volumeControl("org/qtproject/qt/TestClass");
1261 jint fieldValue = volumeControl.getField<jint>("FIELD_NAME");
1262 \endcode
1263*/
1264
1265/*!
1266 \fn template<typename T> T QJniObject::getStaticField(const char *className, const char *fieldName)
1267
1268 Retrieves the value from the static field \a fieldName on the class \a className.
1269*/
1270
1271/*!
1272 \fn template<typename T> T QJniObject::getStaticField(jclass clazz, const char *fieldName)
1273
1274 Retrieves the value from the static field \a fieldName on \a clazz.
1275*/
1276
1277/*!
1278 \fn template <typename Klass, typename T> auto QJniObject::getStaticField(const char *fieldName)
1279
1280 Retrieves the value from the static field \a fieldName for the class \c Klass.
1281
1282 \c Klass needs to be a C++ type with a registered type mapping to a Java type.
1283*/
1284
1285/*!
1286 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, Type value)
1287
1288 Sets the static field \a fieldName of the class \a className to \a value.
1289*/
1290
1291/*!
1292 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, Type value)
1293
1294 Sets the static field \a fieldName of the class \a clazz to \a value.
1295*/
1296
1297/*!
1298 \fn template <typename Klass, typename Ret, typename Type> auto QJniObject::setStaticField(const char *fieldName, Type value)
1299
1300 Sets the static field \a fieldName of the class \c Klass to \a value.
1301
1302 \c Klass needs to be a C++ type with a registered type mapping to a Java type.
1303*/
1304
1305/*!
1306 \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
1307
1308 Retrieves a JNI object from the field \a fieldName with \a signature from
1309 class \a className.
1310
1311 \note This function can be used without a template type.
1312
1313 \code
1314 QJniObject jobj = QJniObject::getStaticObjectField("class/with/Fields", "FIELD_NAME",
1315 "Ljava/lang/String;");
1316 \endcode
1317*/
1318QJniObject QJniObject::getStaticObjectField(const char *className,
1319 const char *fieldName,
1320 const char *signature)
1321{
1322 JNIEnv *env = QJniEnvironment::getJniEnv();
1323 jclass clazz = QJniObject::loadClass(className, env);
1324 if (!clazz)
1325 return QJniObject();
1326 jfieldID id = QJniObject::getCachedFieldID(env, clazz,
1327 className,
1328 fieldName,
1329 signature, true);
1330 if (!id)
1331 return QJniObject();
1332
1333 return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
1334}
1335
1336/*!
1337 \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, const char *signature)
1338
1339 Retrieves a JNI object from the field \a fieldName with \a signature from
1340 class \a clazz.
1341
1342 \note This function can be used without a template type.
1343
1344 \code
1345 QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;");
1346 \endcode
1347*/
1348QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
1349 const char *signature)
1350{
1351 JNIEnv *env = QJniEnvironment::getJniEnv();
1352 jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
1353
1354 const auto clearExceptions = qScopeGuard([env]{
1355 QJniEnvironment::checkAndClearExceptions(env);
1356 });
1357
1358 return getCleanJniObject(getStaticObjectFieldImpl(env, clazz, id), env);
1359}
1360
1361/*!
1362 \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, const char *signature, Type value)
1363
1364 Sets the value of \a fieldName with \a signature to \a value.
1365
1366 \code
1367 QJniObject stringArray = ...;
1368 QJniObject obj = ...;
1369 obj.setObjectField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V",
1370 stringArray.object<jobjectArray>())
1371 \endcode
1372*/
1373
1374/*!
1375 \fn template<typename T> QJniObject QJniObject::getObjectField(const char *fieldName) const
1376
1377 Retrieves a JNI object from the field \a fieldName.
1378
1379 \code
1380 QJniObject field = jniObject.getObjectField<jstring>("FIELD_NAME");
1381 \endcode
1382*/
1383
1384/*!
1385 \fn QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
1386
1387 Retrieves a JNI object from the field \a fieldName with \a signature.
1388
1389 \note This function can be used without a template type.
1390
1391 \code
1392 QJniObject field = jniObject.getObjectField("FIELD_NAME", "Ljava/lang/String;");
1393 \endcode
1394*/
1395QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
1396{
1397 JNIEnv *env = jniEnv();
1398 jfieldID id = getCachedFieldID(env, fieldName, signature);
1399
1400 const auto clearExceptions = qScopeGuard([env]{
1401 QJniEnvironment::checkAndClearExceptions(env);
1402 });
1403
1404 return getCleanJniObject(getObjectFieldImpl(env, id), env);
1405}
1406
1407/*!
1408 \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, Type value)
1409
1410 Sets the value of \a fieldName to \a value.
1411
1412 \code
1413 QJniObject obj;
1414 obj.setField<jint>("AN_INT_FIELD", 10);
1415 jstring myString = ...;
1416 obj.setField<jstring>("A_STRING_FIELD", myString);
1417 \endcode
1418*/
1419
1420/*!
1421 \fn template<typename T> QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
1422
1423 Retrieves the object from the field \a fieldName on the class \a className.
1424
1425 \code
1426 QJniObject jobj = QJniObject::getStaticObjectField<jstring>("class/with/Fields", "FIELD_NAME");
1427 \endcode
1428*/
1429
1430/*!
1431 \fn template<typename T> QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
1432
1433 Retrieves the object from the field \a fieldName on \a clazz.
1434
1435 \code
1436 QJniObject jobj = QJniObject::getStaticObjectField<jstring>(clazz, "FIELD_NAME");
1437 \endcode
1438*/
1439
1440/*!
1441 \fn QJniObject QJniObject::fromString(const QString &string)
1442
1443 Creates a Java string from the QString \a string and returns a QJniObject holding that string.
1444
1445 \code
1446 QString myQString = "QString";
1447 QJniObject myJavaString = QJniObject::fromString(myQString);
1448 \endcode
1449
1450 \sa toString()
1451*/
1452QJniObject QJniObject::fromString(const QString &string)
1453{
1454 JNIEnv *env = QJniEnvironment::getJniEnv();
1455 jstring stringRef = QtJniTypes::Detail::fromQString(string, env);
1456 QJniObject stringObject = getCleanJniObject(stringRef, env);
1457 stringObject.d->m_className = QtJniTypes::Traits<jstring>::className();
1458 stringObject.d->m_is_jstring = QJniObjectPrivate::IsJString::Yes;
1459 return stringObject;
1460}
1461
1462/*!
1463 \fn QString QJniObject::toString() const
1464
1465 Returns a QString with a string representation of the java object.
1466 Calling this function on a Java String object is a convenient way of getting the actual string
1467 data.
1468
1469 \code
1470 QJniObject string = ...; // "Hello Java"
1471 QString qstring = string.toString(); // "Hello Java"
1472 \endcode
1473
1474 \sa fromString()
1475*/
1476QString QJniObject::toString() const
1477{
1478 if (!isValid())
1479 return QString();
1480
1481 JNIEnv *env = jniEnv();
1482 if (d->isJString(env))
1483 return QtJniTypes::Detail::toQString(object<jstring>(), env);
1484
1485 const QJniObject string = callMethod<jstring>("toString");
1486 return QtJniTypes::Detail::toQString(string.object<jstring>(), env);
1487}
1488
1489/*!
1490 \fn bool QJniObject::isClassAvailable(const char *className)
1491
1492 Returns true if the Java class \a className is available.
1493
1494 \code
1495 if (QJniObject::isClassAvailable("java/lang/String")) {
1496 // condition statement
1497 }
1498 \endcode
1499*/
1500bool QJniObject::isClassAvailable(const char *className)
1501{
1502 JNIEnv *env = QJniEnvironment::getJniEnv();
1503
1504 if (!env)
1505 return false;
1506
1507 return loadClass(className, env);
1508}
1509
1510/*!
1511 \fn bool QJniObject::isValid() const
1512
1513 Returns true if this instance holds a valid Java object.
1514
1515 \code
1516 QJniObject qjniObject; // ==> isValid() == false
1517 QJniObject qjniObject(0) // ==> isValid() == false
1518 QJniObject qjniObject("could/not/find/Class") // ==> isValid() == false
1519 \endcode
1520*/
1521bool QJniObject::isValid() const
1522{
1523 return d && d->m_jobject;
1524}
1525
1526/*!
1527 \fn QJniObject QJniObject::fromLocalRef(jobject localRef)
1528
1529 Creates a QJniObject from the local JNI reference \a localRef.
1530 This function takes ownership of \a localRef and frees it before returning.
1531
1532 \note Only call this function with a local JNI reference. For example, most raw JNI calls,
1533 through the JNI environment, return local references to a java object.
1534
1535 \code
1536 jobject localRef = env->GetObjectArrayElement(array, index);
1537 QJniObject element = QJniObject::fromLocalRef(localRef);
1538 \endcode
1539*/
1540QJniObject QJniObject::fromLocalRef(jobject lref)
1541{
1542 QJniObject obj(lref);
1543 obj.jniEnv()->DeleteLocalRef(lref);
1544 return obj;
1545}
1546
1547bool QJniObject::isSameObject(jobject obj) const
1548{
1549 if (!d)
1550 return obj == nullptr;
1551 if (d->m_jobject == obj)
1552 return true;
1553 if (!d->m_jobject || !obj)
1554 return false;
1555 return jniEnv()->IsSameObject(d->m_jobject, obj);
1556}
1557
1558bool QJniObject::isSameObject(const QJniObject &other) const
1559{
1560 if (!other.isValid())
1561 return !isValid();
1562 return isSameObject(other.d->m_jobject);
1563}
1564
1565void QJniObject::assign(jobject obj)
1566{
1567 if (d && isSameObject(obj))
1568 return;
1569
1570 d = QSharedPointer<QJniObjectPrivate>::create();
1571 if (obj) {
1572 JNIEnv *env = jniEnv();
1573 d->m_jobject = env->NewGlobalRef(obj);
1574 jclass objectClass = env->GetObjectClass(obj);
1575 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
1576 env->DeleteLocalRef(objectClass);
1577 }
1578}
1579
1580jobject QJniObject::javaObject() const
1581{
1582 return d->m_jobject;
1583}
1584
1585/*!
1586 \class QtJniTypes::JObjectBase
1587 \brief The JObjectBase in the QtJniTypes namespace is the base of all declared Java types.
1588 \inmodule QtCore
1589 \internal
1590*/
1591
1592/*!
1593 \class QtJniTypes::JObject
1594 \inmodule QtCore
1595 \brief The JObject template in the QtJniTypes namespace is the base of declared Java types.
1596 \since Qt 6.8
1597
1598 JObject<Type> is a template class where \a Type specifies the Java type
1599 being represented.
1600
1601 This template gets specialized when using the Q_DECLARE_JNI_CLASS macro. The
1602 specialization produces a unique type in the QtJniTypes namespace. This
1603 allows the type system to deduce the correct signature in JNI calls when an
1604 instance of the specialized type is passed as a parameter.
1605
1606 Instances can be implicitly converted to and from QJniObject and jobject,
1607 and provide the same template API as QJniObject to call methods and access
1608 properties. Since instances of JObject know about the Java type they hold,
1609 APIs to access static methods or fields do not require the class name as an
1610 explicit parameter.
1611
1612 \sa Q_DECLARE_JNI_CLASS
1613*/
1614
1615/*!
1616 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject()
1617
1618 Default-constructs the JObject instance. This also default-constructs an
1619 instance of the represented Java type.
1620*/
1621
1622/*!
1623 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(const QJniObject &other)
1624
1625 Constructs a JObject instance that holds a reference to the same jobject as \a other.
1626*/
1627
1628/*!
1629 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(jobject other)
1630
1631 Constructs a JObject instance that holds a reference to \a other.
1632*/
1633
1634/*!
1635 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(QJniObject &&other)
1636
1637 Move-constructs a JObject instance from \a other.
1638*/
1639
1640/*!
1641 \fn template <typename Type> bool QtJniTypes::JObject<Type>::isValid() const
1642
1643 Returns whether the JObject instance holds a valid reference to a jobject.
1644
1645 \sa QJniObject::isValid()
1646*/
1647
1648/*!
1649 \fn template <typename Type> jclass QtJniTypes::JObject<Type>::objectClass() const
1650
1651 Returns the Java class that this JObject is an instance of as a jclass.
1652
1653 \sa className(), QJniObject::objectClass()
1654*/
1655
1656/*!
1657 \fn template <typename Type> QString QtJniTypes::JObject<Type>::toString() const
1658
1659 Returns a QString with a string representation of the Java object.
1660
1661 \sa QJniObject::toString()
1662*/
1663
1664/*!
1665 \fn template <typename Type> QByteArray QtJniTypes::JObject<Type>::className() const
1666
1667 Returns the name of the Java class that this object is an instance of.
1668
1669 \sa objectClass(), QJniObject::className()
1670*/
1671
1672/*!
1673 \fn template <typename Type> bool QtJniTypes::JObject<Type>::isClassAvailable()
1674
1675 Returns whether the class that this JObject specialization represents is
1676 available.
1677
1678 \sa QJniObject::isClassAvailable()
1679*/
1680
1681/*!
1682 \fn template <typename Type> JObject QtJniTypes::JObject<Type>::fromJObject(jobject object)
1683
1684 Constructs a JObject instance from \a object and returns that instance.
1685*/
1686
1687/*!
1688 \fn template <typename Type> template <typename ...Args> JObject QtJniTypes::JObject<Type>::construct(Args &&...args)
1689
1690 Constructs a Java object from \a args and returns a JObject instance that
1691 holds a reference to that Java object.
1692*/
1693
1694/*!
1695 \fn template <typename Type> JObject QtJniTypes::JObject<Type>::fromLocalRef(jobject ref)
1696
1697 Constructs a JObject that holds a local reference to \a ref, and returns
1698 that object.
1699*/
1700
1701/*!
1702 \fn template <typename Type> template <typename Ret, typename ...Args> auto QtJniTypes::JObject<Type>::callStaticMethod(const char *methodName, Args &&...args)
1703
1704 Calls the static method \a methodName with arguments \a args, and returns
1705 the result of type Ret (unless Ret is \c void). If Ret is a
1706 jobject type, then the returned value will be a QJniObject.
1707
1708 \sa QJniObject::callStaticMethod()
1709*/
1710
1711/*!
1712 \fn template <typename Type> bool QtJniTypes::JObject<Type>::registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
1713
1714 Registers the Java methods in \a methods with the Java class represented by
1715 the JObject specialization, and returns whether the registration was successful.
1716
1717 \sa QJniEnvironment::registerNativeMethods()
1718*/
1719
1720/*!
1721 \fn template <typename Type> template <typename T> auto QtJniTypes::JObject<Type>::getStaticField(const char *field)
1722
1723 Returns the value of the static field \a field.
1724
1725 \sa QJniObject::getStaticField()
1726*/
1727
1728/*!
1729 \fn template <typename Type> template <typename Ret, typename T> auto QtJniTypes::JObject<Type>::setStaticField(const char *field, T &&value)
1730
1731 Sets the static field \a field to \a value.
1732
1733 \sa QJniObject::setStaticField()
1734*/
1735
1736/*!
1737 \fn template <typename Type> template <typename Ret, typename ...Args> auto QtJniTypes::JObject<Type>::callMethod(const char *method, Args &&...args) const
1738
1739 Calls the instance method \a method with arguments \a args, and returns
1740 the result of type Ret (unless Ret is \c void). If Ret is a
1741 jobject type, then the returned value will be a QJniObject.
1742
1743 \sa QJniObject::callMethod()
1744*/
1745
1746/*!
1747 \fn template <typename Type> template <typename T> auto QtJniTypes::JObject<Type>::getField(const char *field) const
1748
1749 Returns the value of the instance field \a field.
1750
1751 \sa QJniObject::getField()
1752*/
1753
1754/*!
1755 \fn template <typename Type> template <typename Ret, typename T> auto QtJniTypes::JObject<Type>::setField(const char *field, T &&value)
1756
1757 Sets the value of the instance field \a field to \a value.
1758
1759 \sa QJniObject::setField()
1760*/
1761
1762
1763QT_END_NAMESPACE
bool isJString(JNIEnv *env) const
IsJString m_is_jstring
QByteArray m_className
void construct(const char *signature=nullptr, Args &&...args)
Combined button and popup list for selecting options.
static QByteArray cacheKey(Args &&...args)
jmethodID loadClassMethod