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