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 // Whether m_className is the name Qt resolved the class with.
374 bool m_resolvedByName = false;
375 enum class IsJString : quint8 {
379 };
381};
382
383// The separator keeps the concatenation unambiguous.
384template <typename ...Args>
385static inline QByteArray cacheKey(Args &&...args)
386{
387 return ((QByteArrayView(":") + QByteArrayView(args)) + ...);
388}
389
394Q_GLOBAL_STATIC(JClassHash, cachedClasses)
395Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
396
397static jclass getCachedClass(const QByteArray &className)
398{
399 QReadLocker locker(cachedClassesLock);
400 const auto &it = cachedClasses->constFind(className);
401 return it != cachedClasses->constEnd() ? it.value() : nullptr;
402}
403
404
405bool QJniObjectPrivate::isJString(JNIEnv *env) const
406{
407 Q_ASSERT(m_jobject);
408 if (m_is_jstring != IsJString::Unknown)
409 return m_is_jstring == IsJString::Yes;
410
411 static constexpr auto stringSignature = QtJniTypes::Traits<jstring>::className();
412 if (!m_className.isEmpty()) {
413 m_is_jstring = m_className == stringSignature
414 ? IsJString::Yes
415 : IsJString::No;
416 } else {
417 const jclass strClass = getCachedClass(stringSignature.data());
418 const bool isStringInstance = strClass
419 ? env->IsInstanceOf(m_jobject, strClass)
420 : false;
421 m_is_jstring = isStringInstance ? IsJString::Yes : IsJString::No;
422 }
423
424 return m_is_jstring == IsJString::Yes;
425}
426
427/*!
428 \internal
429
430 Get a JNI object from a jobject variant and do the necessary
431 exception clearing and delete the local reference before returning.
432 The JNI object can be null if there was an exception.
433*/
434static QJniObject getCleanJniObject(jobject object, JNIEnv *env)
435{
436 if (!object || env->ExceptionCheck())
437 return QJniObject();
438
439 return QJniObject::fromLocalRef(object);
440}
441
442namespace {
443
444jclass loadClassHelper(const QByteArray &className, JNIEnv *env)
445{
446 Q_ASSERT(env);
447 QByteArray classNameArray(className);
448#ifdef QT_DEBUG
449 if (classNameArray.contains('.')) {
450 qWarning("QtAndroidPrivate::findClass: className '%s' should use slash separators!",
451 className.constData());
452 }
453#endif
454 classNameArray.replace('.', '/');
455 jclass clazz = getCachedClass(classNameArray);
456 if (clazz)
457 return clazz;
458
459 QWriteLocker locker(cachedClassesLock);
460 // Check again; another thread might have added the class to the cache after
461 // our call to getCachedClass and before we acquired the lock.
462 const auto &it = cachedClasses->constFind(classNameArray);
463 if (it != cachedClasses->constEnd())
464 return it.value();
465
466 // JNIEnv::FindClass wants "a fully-qualified class name or an array type signature"
467 // which is a slash-separated class name.
468 jclass localClazz = env->FindClass(classNameArray.constData());
469 if (localClazz) {
470 clazz = static_cast<jclass>(env->NewGlobalRef(localClazz));
471 env->DeleteLocalRef(localClazz);
472 } else {
473 // Clear the exception silently; we are going to try the ClassLoader next,
474 // so no need for warning unless that fails as well.
475 env->ExceptionClear();
476 }
477
478 if (!clazz) {
479 // Wrong class loader, try our own
480 jobject classLoader = QtAndroidPrivate::classLoader();
481 if (!classLoader)
482 return nullptr;
483
484 if (!cachedClasses->loadClassMethod) {
485 jclass classLoaderClass = env->GetObjectClass(classLoader);
486 if (!classLoaderClass)
487 return nullptr;
488 cachedClasses->loadClassMethod =
489 env->GetMethodID(classLoaderClass,
490 "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
491 env->DeleteLocalRef(classLoaderClass);
492 if (!cachedClasses->loadClassMethod) {
493 qCritical("Couldn't find the 'loadClass' method in the Qt class loader");
494 return nullptr;
495 }
496 }
497
498 // ClassLoader::loadClass on the other hand wants the binary name of the class,
499 // e.g. dot-separated. In testing it works also with /, but better to stick to
500 // the specification.
501 const QString binaryClassName = QString::fromLatin1(className).replace(u'/', u'.');
502 jstring classNameObject = env->NewString(binaryClassName.utf16(), binaryClassName.length());
503 jobject classObject = env->CallObjectMethod(classLoader,
504 cachedClasses->loadClassMethod,
505 classNameObject);
506 env->DeleteLocalRef(classNameObject);
507
508 if (classObject && !env->ExceptionCheck()) {
509 clazz = static_cast<jclass>(env->NewGlobalRef(classObject));
510 env->DeleteLocalRef(classObject);
511 }
512 // Clearing the exception is the caller's responsibility (see
513 // QtAndroidPrivate::findClass()) and QJniObject::loadClass{KeepExceptions}
514 }
515
516 if (clazz)
517 cachedClasses->insert(classNameArray, clazz);
518
519 return clazz;
520}
521
522} // unnamed namespace
523
524/*!
525 \internal
526 \a className must be slash-encoded
527*/
528jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
529{
530 jclass clazz = loadClassHelper(className, env);
531 if (!clazz)
532 QJniEnvironment::checkAndClearExceptions(env);
533 return clazz;
534}
535
536jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env)
537{
538 return QtAndroidPrivate::findClass(className, env);
539}
540
541jclass QJniObject::loadClassKeepExceptions(const QByteArray &className, JNIEnv *env)
542{
543 return loadClassHelper(className, env);
544}
545
546
547
548typedef QHash<QByteArray, jmethodID> JMethodIDHash;
549Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
550Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
551
552jmethodID QJniObject::getMethodID(JNIEnv *env,
553 jclass clazz,
554 const char *name,
555 const char *signature,
556 bool isStatic)
557{
558 jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
559 : env->GetMethodID(clazz, name, signature);
560
561 return id;
562}
563
564void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
565{
566 va_list args;
567 va_start(args, id);
568 env->CallVoidMethodV(d->m_jobject, id, args);
569 va_end(args);
570}
571
572jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
573 jclass clazz,
574 const QByteArray &className,
575 const char *name,
576 const char *signature,
577 bool isStatic)
578{
579 if (className.isEmpty())
580 return getMethodID(env, clazz, name, signature, isStatic);
581
582 const QByteArray key = cacheKey(className, name, signature,
583 isStatic ? "static" : "instance");
584 QHash<QByteArray, jmethodID>::const_iterator it;
585
586 {
587 QReadLocker locker(cachedMethodIDLock);
588 it = cachedMethodID->constFind(key);
589 if (it != cachedMethodID->constEnd())
590 return it.value();
591 }
592
593 {
594 QWriteLocker locker(cachedMethodIDLock);
595 it = cachedMethodID->constFind(key);
596 if (it != cachedMethodID->constEnd())
597 return it.value();
598
599 jmethodID id = getMethodID(env, clazz, name, signature, isStatic);
600
601 if (id)
602 cachedMethodID->insert(key, id);
603 return id;
604 }
605}
606
607jmethodID QJniObject::getCachedMethodID(JNIEnv *env, const char *name,
608 const char *signature, bool isStatic) const
609{
610 // A class Qt did not resolve by name may share its name with another
611 // loader's, so it cannot share the name-keyed cache.
612 const QByteArray className = d->m_resolvedByName ? d->m_className : QByteArray();
613 return QJniObject::getCachedMethodID(env, d->m_jclass, className, name, signature, isStatic);
614}
615
616typedef QHash<QByteArray, jfieldID> JFieldIDHash;
617Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
618Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
619
620jfieldID QJniObject::getFieldID(JNIEnv *env,
621 jclass clazz,
622 const char *name,
623 const char *signature,
624 bool isStatic)
625{
626 jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
627 : env->GetFieldID(clazz, name, signature);
628
629 return id;
630}
631
632jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
633 jclass clazz,
634 const QByteArray &className,
635 const char *name,
636 const char *signature,
637 bool isStatic)
638{
639 if (className.isEmpty())
640 return getFieldID(env, clazz, name, signature, isStatic);
641
642 const QByteArray key = cacheKey(className, name, signature,
643 isStatic ? "static" : "instance");
644 QHash<QByteArray, jfieldID>::const_iterator it;
645
646 {
647 QReadLocker locker(cachedFieldIDLock);
648 it = cachedFieldID->constFind(key);
649 if (it != cachedFieldID->constEnd())
650 return it.value();
651 }
652
653 {
654 QWriteLocker locker(cachedFieldIDLock);
655 it = cachedFieldID->constFind(key);
656 if (it != cachedFieldID->constEnd())
657 return it.value();
658
659 jfieldID id = getFieldID(env, clazz, name, signature, isStatic);
660
661 if (id)
662 cachedFieldID->insert(key, id);
663 return id;
664 }
665}
666
667jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
668 const char *name,
669 const char *signature,
670 bool isStatic) const
671{
672 const QByteArray className = d->m_resolvedByName ? d->m_className : QByteArray();
673 return QJniObject::getCachedFieldID(env, d->m_jclass, className, name, signature, isStatic);
674}
675
676/*!
677 \fn QJniObject::QJniObject()
678
679 Constructs an invalid JNI object.
680
681 \sa isValid()
682*/
683QJniObject::QJniObject()
684 : d(new QJniObjectPrivate())
685{
686}
687
688/*!
689 \fn QJniObject::QJniObject(const char *className)
690
691 Constructs a new JNI object by calling the default constructor of \a className.
692
693 \code
694 QJniObject myJavaString("java/lang/String");
695 \endcode
696*/
697QJniObject::QJniObject(const char *className)
698 : d(new QJniObjectPrivate())
699{
700 d->m_className = className;
701 d->m_jclass = loadClass(d->m_className, jniEnv());
702 d->m_own_jclass = false;
703 d->m_resolvedByName = true;
704
705 d->construct();
706}
707
708/*!
709 \fn QJniObject::QJniObject(const char *className, const char *signature, ...)
710
711 Constructs a new JNI object by calling the constructor of \a className with
712 \a signature specifying the types of any subsequent arguments.
713
714 \code
715 QJniEnvironment env;
716 char* str = "Hello";
717 jstring myJStringArg = env->NewStringUTF(str);
718 QJniObject myNewJavaString("java/lang/String", "(Ljava/lang/String;)V", myJStringArg);
719 \endcode
720*/
721QJniObject::QJniObject(const char *className, const char *signature, ...)
722 : d(new QJniObjectPrivate())
723{
724 d->m_className = className;
725 d->m_jclass = loadClass(d->m_className, jniEnv());
726 d->m_own_jclass = false;
727 d->m_resolvedByName = true;
728
729 va_list args;
730 va_start(args, signature);
731 d->construct(signature, args);
732 va_end(args);
733}
734
735/*!
736 \fn template<typename ...Args> QJniObject::QJniObject(const char *className, Args &&...args)
737 \since 6.4
738
739 Constructs a new JNI object by calling the constructor of \a className with
740 the arguments \a args. This constructor is only available if all \a args are
741 known \l {JNI Types}.
742
743 \code
744 QJniEnvironment env;
745 char* str = "Hello";
746 jstring myJStringArg = env->NewStringUTF(str);
747 QJniObject myNewJavaString("java/lang/String", myJStringArg);
748 \endcode
749*/
750
751/*!
752 Constructs a new JNI object from \a clazz by calling the constructor with
753 \a signature specifying the types of any subsequent arguments.
754
755 \code
756 QJniEnvironment env;
757 jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
758 QJniObject(myClazz, "(I)V", 3);
759 \endcode
760*/
761QJniObject::QJniObject(jclass clazz, const char *signature, ...)
762 : d(new QJniObjectPrivate())
763{
764 if (clazz) {
765 d->m_jclass = static_cast<jclass>(jniEnv()->NewGlobalRef(clazz));
766 va_list args;
767 va_start(args, signature);
768 d->construct(signature, args);
769 va_end(args);
770 }
771}
772
773/*!
774 \fn template<typename ...Args> QJniObject::QJniObject(jclass clazz, Args &&...args)
775 \since 6.4
776
777 Constructs a new JNI object from \a clazz by calling the constructor with
778 the arguments \a args. This constructor is only available if all \a args are
779 known \l {JNI Types}.
780
781 \code
782 QJniEnvironment env;
783 jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
784 QJniObject(myClazz, 3);
785 \endcode
786*/
787
788/*!
789 Constructs a new JNI object by calling the default constructor of \a clazz.
790
791 \note The QJniObject will create a new reference to the class \a clazz
792 and releases it again when it is destroyed. References to the class created
793 outside the QJniObject need to be managed by the caller.
794*/
795
796QJniObject::QJniObject(jclass clazz)
797 : QJniObject(clazz, "()V")
798{
799}
800
801/*!
802 Constructs a new JNI object around the Java object \a object.
803
804 \note The QJniObject will hold a reference to the Java object \a object
805 and release it when destroyed. Any references to the Java object \a object
806 outside QJniObject needs to be managed by the caller. In most cases you
807 should never call this function with a local reference unless you intend
808 to manage the local reference yourself. See QJniObject::fromLocalRef()
809 for converting a local reference to a QJniObject.
810
811 \sa fromLocalRef()
812*/
813QJniObject::QJniObject(jobject object)
814 : d(new QJniObjectPrivate())
815{
816 if (!object)
817 return;
818
819 JNIEnv *env = jniEnv();
820 d->m_jobject = env->NewGlobalRef(object);
821 jclass cls = env->GetObjectClass(object);
822 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
823 env->DeleteLocalRef(cls);
824}
825
826/*!
827 \fn template<typename Class, typename ...Args> static inline QJniObject QJniObject::construct(Args &&...args)
828 \since 6.4
829
830 Constructs an instance of the Java class that is the equivalent of \c Class and
831 returns a QJniObject containing the JNI object. The arguments in \a args are
832 passed to the Java constructor.
833
834 \code
835 QJniObject javaString = QJniObject::construct<jstring>();
836 \endcode
837
838 This function is only available if all \a args are known \l {JNI Types}.
839*/
840
841/*!
842 \fn QJniObject::~QJniObject()
843
844 Destroys the JNI object and releases any references held by the JNI object.
845*/
846QJniObject::~QJniObject()
847{}
848
849/*!
850 \fn void QJniObject::swap(QJniObject &other)
851 \since 6.8
852 \memberswap{object}
853*/
854
855
856namespace {
857QByteArray getClassNameHelper(JNIEnv *env, const QJniObjectPrivate *d)
858{
859 if (env->PushLocalFrame(3) != JNI_OK) // JVM out of memory
860 return QByteArray();
861
862 jmethodID mid = env->GetMethodID(d->m_jclass, "getClass", "()Ljava/lang/Class;");
863 jobject classObject = env->CallObjectMethod(d->m_jobject, mid);
864 jclass classObjectClass = env->GetObjectClass(classObject);
865 mid = env->GetMethodID(classObjectClass, "getName", "()Ljava/lang/String;");
866 jstring stringObject = static_cast<jstring>(env->CallObjectMethod(classObject, mid));
867 const jsize length = env->GetStringUTFLength(stringObject);
868 const char* nameString = env->GetStringUTFChars(stringObject, NULL);
869 const QByteArray result = QByteArray::fromRawData(nameString, length).replace('.', '/');
870 env->ReleaseStringUTFChars(stringObject, nameString);
871 env->PopLocalFrame(nullptr);
872 return result;
873}
874}
875
876/*! \internal
877
878 Returns the JNIEnv of the calling thread.
879*/
880JNIEnv *QJniObject::jniEnv() const noexcept
881{
882 return QJniEnvironment::getJniEnv();
883}
884
885/*!
886 \fn jobject QJniObject::object() const
887 \fn template <typename T> T QJniObject::object() const
888
889 Returns the object held by the QJniObject either as jobject or as type T.
890 T can be one of \l {Object Types}{JNI Object Types}.
891
892 \code
893 QJniObject string = QJniObject::fromString("Hello, JNI");
894 jstring jstring = string.object<jstring>();
895 \endcode
896
897 \note The returned object is still kept alive by this QJniObject. To keep the
898 object alive beyond the lifetime of this QJniObject, for example to record it
899 for later use, the easiest approach is to store it in another QJniObject with
900 a suitable lifetime. Alternatively, you may create a new global reference to the
901 object and store it, taking care to free it when you are done with it.
902
903 \snippet jni/src_qjniobject.cpp QJniObject scope
904*/
905jobject QJniObject::object() const
906{
907 return javaObject();
908}
909
910/*!
911 \fn jclass QJniObject::objectClass() const
912
913 Returns the class object held by the QJniObject as a \c jclass.
914
915 \note The returned object is still kept alive by this QJniObject. To keep the
916 object alive beyond the lifetime of this QJniObject, for example to record it
917 for later use, the easiest approach is to store it in another QJniObject with
918 a suitable lifetime. Alternatively, you may create a new global reference to the
919 object and store it, taking care to free it when you are done with it.
920
921 \since 6.2
922*/
923jclass QJniObject::objectClass() const
924{
925 return d->m_jclass;
926}
927
928/*!
929 \fn QByteArray QJniObject::className() const
930
931 Returns the name of the class object held by the QJniObject as a \c QByteArray.
932
933 \since 6.2
934*/
935QByteArray QJniObject::className() const
936{
937 if (d->m_className.isEmpty() && d->m_jclass && d->m_jobject) {
938 JNIEnv *env = jniEnv();
939 d->m_className = getClassNameHelper(env, d.get());
940 }
941 return d->m_className;
942}
943
944/*!
945 \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, const char *signature, Args &&...args) const
946 \since 6.4
947
948 Calls the object's method \a methodName with \a signature specifying the types of any
949 subsequent arguments \a args, and returns the value (unless Ret is \c void). If Ret
950 is a jobject type, then the returned value will be a QJniObject.
951
952 \code
953 QJniObject myJavaString("org/qtproject/qt/TestClass");
954 jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051);
955 \endcode
956*/
957
958/*!
959 \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, Args &&...args) const
960 \since 6.4
961
962 Calls the method \a methodName with arguments \a args and returns the value
963 (unless Ret is \c void). If Ret is a jobject type, then the returned value
964 will be a QJniObject.
965
966 \code
967 QJniObject myJavaString("org/qtproject/qt/TestClass");
968 jint size = myJavaString.callMethod<jint>("length");
969 \endcode
970
971 The method signature is deduced at compile time from Ret and the types
972 of \a args. Ret can be a \c{std::expected}-compatible type that returns
973 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
974 called method.
975*/
976
977/*!
978 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
979 \since 6.4
980
981 Calls the static method \a methodName from class \a className with \a signature
982 specifying the types of any subsequent arguments \a args. Returns the result of
983 the method (unless Ret is \c void). If Ret is a jobject type, then the
984 returned value will be a QJniObject.
985
986 \code
987 jint a = 2;
988 jint b = 4;
989 jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
990 \endcode
991*/
992
993/*!
994 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, Args &&...args)
995 \since 6.4
996
997 Calls the static method \a methodName on class \a className with arguments \a args,
998 and returns the value of type Ret (unless Ret is \c void). If Ret
999 is a jobject type, then the returned value will be a QJniObject.
1000
1001 \code
1002 jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
1003 \endcode
1004
1005 The method signature is deduced at compile time from Ret and the types
1006 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1007 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1008 called method.
1009*/
1010
1011/*!
1012 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
1013
1014 Calls the static method \a methodName from \a clazz with \a signature
1015 specifying the types of any subsequent arguments. Returns the result of
1016 the method (unless Ret is \c void). If Ret is a jobject type, then the
1017 returned value will be a QJniObject.
1018
1019 \code
1020 QJniEnvironment env;
1021 jclass javaMathClass = env.findClass("java/lang/Math");
1022 jint a = 2;
1023 jint b = 4;
1024 jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b);
1025 \endcode
1026*/
1027
1028/*!
1029 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
1030 \since 6.4
1031
1032 Calls the static method identified by \a methodId from the class \a clazz
1033 with any subsequent arguments, and returns the value of type Ret (unless
1034 Ret is \c void). If Ret is a jobject type, then the returned value will
1035 be a QJniObject.
1036
1037 Useful when \a clazz and \a methodId are already cached from previous operations.
1038
1039 \code
1040 QJniEnvironment env;
1041 jclass javaMathClass = env.findClass("java/lang/Math");
1042 jmethodID methodId = env.findStaticMethod(javaMathClass, "max", "(II)I");
1043 if (methodId != 0) {
1044 jint a = 2;
1045 jint b = 4;
1046 jint max = QJniObject::callStaticMethod<jint>(javaMathClass, methodId, a, b);
1047 }
1048 \endcode
1049*/
1050
1051/*!
1052 \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
1053 \since 6.4
1054
1055 Calls the static method \a methodName on \a clazz and returns the value of type Ret
1056 (unless Ret is \c void). If Ret is a jobject type, then the returned value will
1057 be a QJniObject.
1058
1059 \code
1060 QJniEnvironment env;
1061 jclass javaMathClass = env.findClass("java/lang/Math");
1062 jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
1063 \endcode
1064
1065 The method signature is deduced at compile time from Ret and the types
1066 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1067 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1068 called method.
1069*/
1070
1071/*!
1072 \fn template <typename Klass, typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *methodName, Args &&...args)
1073 \since 6.7
1074
1075 Calls the static method \a methodName on the class \c Klass and returns the value of type
1076 Ret (unless Ret is \c void). If Ret is a jobject type, then the returned value will
1077 be a QJniObject.
1078
1079 The method signature is deduced at compile time from Ret and the types
1080 of \a args. \c Klass needs to be a C++ type with a registered type mapping
1081 to a Java type. Ret can be a \c{std::expected}-compatible type that
1082 returns a value, or \l{Handling Java Exceptions}{any Java exception thrown}
1083 by the called method.
1084
1085*/
1086
1087/*!
1088 \fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
1089
1090 Calls the Java object's method \a methodName with \a signature specifying
1091 the types of any subsequent arguments.
1092
1093 \code
1094 QJniObject myJavaString = QJniObject::fromString("Hello, Java");
1095 QJniObject mySubstring = myJavaString.callObjectMethod("substring",
1096 "(II)Ljava/lang/String;", 7, 11);
1097 \endcode
1098*/
1099QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
1100{
1101 JNIEnv *env = jniEnv();
1102 jmethodID id = getCachedMethodID(env, methodName, signature);
1103 va_list args;
1104 va_start(args, signature);
1105 // can't go back from variadic arguments to variadic templates
1106 jobject object = id ? jniEnv()->CallObjectMethodV(javaObject(), id, args) : nullptr;
1107 QJniObject res = getCleanJniObject(object, env);
1108 va_end(args);
1109
1110 QJniEnvironment::checkAndClearExceptions(env);
1111 return res;
1112}
1113
1114/*!
1115 \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...)
1116
1117 Calls the static method \a methodName from the class \a className with \a signature
1118 specifying the types of any subsequent arguments.
1119
1120 \code
1121 QJniObject thread = QJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread",
1122 "()Ljava/lang/Thread;");
1123 QJniObject string = QJniObject::callStaticObjectMethod("java/lang/String", "valueOf",
1124 "(I)Ljava/lang/String;", 10);
1125 \endcode
1126*/
1127QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
1128 const char *signature, ...)
1129{
1130 JNIEnv *env = QJniEnvironment::getJniEnv();
1131 jclass clazz = QJniObject::loadClass(className, env);
1132 if (clazz) {
1133 jmethodID id = QJniObject::getCachedMethodID(env, clazz,
1134 className,
1135 methodName, signature, true);
1136 if (id) {
1137 va_list args;
1138 va_start(args, signature);
1139 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env);
1140 va_end(args);
1141 return res;
1142 }
1143 }
1144
1145 return QJniObject();
1146}
1147
1148/*!
1149 \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...)
1150
1151 Calls the static method \a methodName from class \a clazz with \a signature
1152 specifying the types of any subsequent arguments.
1153*/
1154QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName,
1155 const char *signature, ...)
1156{
1157 if (clazz) {
1158 QJniEnvironment env;
1159 jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
1160 if (id) {
1161 va_list args;
1162 va_start(args, signature);
1163 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env.jniEnv());
1164 va_end(args);
1165 return res;
1166 }
1167 }
1168
1169 return QJniObject();
1170}
1171
1172/*!
1173 \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
1174
1175 Calls the static method identified by \a methodId from the class \a clazz
1176 with any subsequent arguments. Useful when \a clazz and \a methodId are
1177 already cached from previous operations.
1178
1179 \code
1180 QJniEnvironment env;
1181 jclass clazz = env.findClass("java/lang/String");
1182 jmethodID methodId = env.findStaticMethod(clazz, "valueOf", "(I)Ljava/lang/String;");
1183 if (methodId != 0)
1184 QJniObject str = QJniObject::callStaticObjectMethod(clazz, methodId, 10);
1185 \endcode
1186*/
1187QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
1188{
1189 if (clazz && methodId) {
1190 JNIEnv *env = QJniEnvironment::getJniEnv();
1191 va_list args;
1192 va_start(args, methodId);
1193 QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args), env);
1194 va_end(args);
1195 return res;
1196 }
1197
1198 return QJniObject();
1199}
1200
1201/*!
1202 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callObjectMethod(const char *methodName, Args &&...args) const
1203 \since 6.4
1204
1205 Calls the Java objects method \a methodName with arguments \a args and returns a
1206 new QJniObject for the returned Java object.
1207
1208 \code
1209 QJniObject myJavaString = QJniObject::fromString("Hello, Java");
1210 QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
1211 \endcode
1212
1213 The method signature is deduced at compile time from Ret and the types
1214 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1215 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1216 called method.
1217*/
1218
1219/*!
1220 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
1221 \since 6.4
1222
1223 Calls the static method with \a methodName on the class \a className, passing
1224 arguments \a args, and returns a new QJniObject for the returned Java object.
1225
1226 \code
1227 QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
1228 \endcode
1229
1230 The method signature is deduced at compile time from Ret and the types
1231 of \a args. Ret can be a \c{std::expected}-compatible type that returns
1232 a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the
1233 called method.
1234*/
1235
1236/*!
1237 \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
1238 \since 6.4
1239
1240 Calls the static method with \a methodName on \a clazz, passing arguments \a args,
1241 and returns a new QJniObject for the returned Java object.
1242*/
1243
1244/*!
1245 \fn template <typename T, std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true> QJniObject &QJniObject::operator=(T object)
1246
1247 Replace the current object with \a object. The old Java object will be released.
1248*/
1249
1250/*!
1251 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, Type value);
1252
1253 Sets the static field \a fieldName on the class \a className to \a value
1254 using the setter with \a signature.
1255
1256*/
1257
1258/*!
1259 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, Type value);
1260
1261 Sets the static field \a fieldName on the class \a clazz to \a value using
1262 the setter with \a signature.
1263*/
1264
1265/*!
1266 \fn template<typename T> T QJniObject::getField(const char *fieldName) const
1267
1268 Retrieves the value of the field \a fieldName.
1269
1270 \code
1271 QJniObject volumeControl("org/qtproject/qt/TestClass");
1272 jint fieldValue = volumeControl.getField<jint>("FIELD_NAME");
1273 \endcode
1274*/
1275
1276/*!
1277 \fn template<typename T> T QJniObject::getStaticField(const char *className, const char *fieldName)
1278
1279 Retrieves the value from the static field \a fieldName on the class \a className.
1280*/
1281
1282/*!
1283 \fn template<typename T> T QJniObject::getStaticField(jclass clazz, const char *fieldName)
1284
1285 Retrieves the value from the static field \a fieldName on \a clazz.
1286*/
1287
1288/*!
1289 \fn template <typename Klass, typename T> auto QJniObject::getStaticField(const char *fieldName)
1290
1291 Retrieves the value from the static field \a fieldName for the class \c Klass.
1292
1293 \c Klass needs to be a C++ type with a registered type mapping to a Java type.
1294*/
1295
1296/*!
1297 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(const char *className, const char *fieldName, Type value)
1298
1299 Sets the static field \a fieldName of the class \a className to \a value.
1300*/
1301
1302/*!
1303 \fn template <typename Ret, typename Type> auto QJniObject::setStaticField(jclass clazz, const char *fieldName, Type value)
1304
1305 Sets the static field \a fieldName of the class \a clazz to \a value.
1306*/
1307
1308/*!
1309 \fn template <typename Klass, typename Ret, typename Type> auto QJniObject::setStaticField(const char *fieldName, Type value)
1310
1311 Sets the static field \a fieldName of the class \c Klass to \a value.
1312
1313 \c Klass needs to be a C++ type with a registered type mapping to a Java type.
1314*/
1315
1316/*!
1317 \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
1318
1319 Retrieves a JNI object from the field \a fieldName with \a signature from
1320 class \a className.
1321
1322 \note This function can be used without a template type.
1323
1324 \code
1325 QJniObject jobj = QJniObject::getStaticObjectField("class/with/Fields", "FIELD_NAME",
1326 "Ljava/lang/String;");
1327 \endcode
1328*/
1329QJniObject QJniObject::getStaticObjectField(const char *className,
1330 const char *fieldName,
1331 const char *signature)
1332{
1333 JNIEnv *env = QJniEnvironment::getJniEnv();
1334 jclass clazz = QJniObject::loadClass(className, env);
1335 if (!clazz)
1336 return QJniObject();
1337 jfieldID id = QJniObject::getCachedFieldID(env, clazz,
1338 className,
1339 fieldName,
1340 signature, true);
1341 if (!id)
1342 return QJniObject();
1343
1344 return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
1345}
1346
1347/*!
1348 \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, const char *signature)
1349
1350 Retrieves a JNI object from the field \a fieldName with \a signature from
1351 class \a clazz.
1352
1353 \note This function can be used without a template type.
1354
1355 \code
1356 QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;");
1357 \endcode
1358*/
1359QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
1360 const char *signature)
1361{
1362 JNIEnv *env = QJniEnvironment::getJniEnv();
1363 jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
1364
1365 const auto clearExceptions = qScopeGuard([env]{
1366 QJniEnvironment::checkAndClearExceptions(env);
1367 });
1368
1369 return getCleanJniObject(getStaticObjectFieldImpl(env, clazz, id), env);
1370}
1371
1372/*!
1373 \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, const char *signature, Type value)
1374
1375 Sets the value of \a fieldName with \a signature to \a value.
1376
1377 \code
1378 QJniObject stringArray = ...;
1379 QJniObject obj = ...;
1380 obj.setObjectField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V",
1381 stringArray.object<jobjectArray>())
1382 \endcode
1383*/
1384
1385/*!
1386 \fn template<typename T> QJniObject QJniObject::getObjectField(const char *fieldName) const
1387
1388 Retrieves a JNI object from the field \a fieldName.
1389
1390 \code
1391 QJniObject field = jniObject.getObjectField<jstring>("FIELD_NAME");
1392 \endcode
1393*/
1394
1395/*!
1396 \fn QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
1397
1398 Retrieves a JNI object from the field \a fieldName with \a signature.
1399
1400 \note This function can be used without a template type.
1401
1402 \code
1403 QJniObject field = jniObject.getObjectField("FIELD_NAME", "Ljava/lang/String;");
1404 \endcode
1405*/
1406QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
1407{
1408 JNIEnv *env = jniEnv();
1409 jfieldID id = getCachedFieldID(env, fieldName, signature);
1410
1411 const auto clearExceptions = qScopeGuard([env]{
1412 QJniEnvironment::checkAndClearExceptions(env);
1413 });
1414
1415 return getCleanJniObject(getObjectFieldImpl(env, id), env);
1416}
1417
1418/*!
1419 \fn template <typename Ret, typename Type> void QJniObject::setField(const char *fieldName, Type value)
1420
1421 Sets the value of \a fieldName to \a value.
1422
1423 \code
1424 QJniObject obj;
1425 obj.setField<jint>("AN_INT_FIELD", 10);
1426 jstring myString = ...;
1427 obj.setField<jstring>("A_STRING_FIELD", myString);
1428 \endcode
1429*/
1430
1431/*!
1432 \fn template<typename T> QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
1433
1434 Retrieves the object from the field \a fieldName on the class \a className.
1435
1436 \code
1437 QJniObject jobj = QJniObject::getStaticObjectField<jstring>("class/with/Fields", "FIELD_NAME");
1438 \endcode
1439*/
1440
1441/*!
1442 \fn template<typename T> QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
1443
1444 Retrieves the object from the field \a fieldName on \a clazz.
1445
1446 \code
1447 QJniObject jobj = QJniObject::getStaticObjectField<jstring>(clazz, "FIELD_NAME");
1448 \endcode
1449*/
1450
1451/*!
1452 \fn QJniObject QJniObject::fromString(const QString &string)
1453
1454 Creates a Java string from the QString \a string and returns a QJniObject holding that string.
1455
1456 \code
1457 QString myQString = "QString";
1458 QJniObject myJavaString = QJniObject::fromString(myQString);
1459 \endcode
1460
1461 \sa toString()
1462*/
1463QJniObject QJniObject::fromString(const QString &string)
1464{
1465 JNIEnv *env = QJniEnvironment::getJniEnv();
1466 jstring stringRef = QtJniTypes::Detail::fromQString(string, env);
1467 QJniObject stringObject = getCleanJniObject(stringRef, env);
1468 stringObject.d->m_className = QtJniTypes::Traits<jstring>::className();
1469 stringObject.d->m_resolvedByName = true;
1470 stringObject.d->m_is_jstring = QJniObjectPrivate::IsJString::Yes;
1471 return stringObject;
1472}
1473
1474/*!
1475 \fn QString QJniObject::toString() const
1476
1477 Returns a QString with a string representation of the java object.
1478 Calling this function on a Java String object is a convenient way of getting the actual string
1479 data.
1480
1481 \code
1482 QJniObject string = ...; // "Hello Java"
1483 QString qstring = string.toString(); // "Hello Java"
1484 \endcode
1485
1486 \sa fromString()
1487*/
1488QString QJniObject::toString() const
1489{
1490 if (!isValid())
1491 return QString();
1492
1493 JNIEnv *env = jniEnv();
1494 if (d->isJString(env))
1495 return QtJniTypes::Detail::toQString(object<jstring>(), env);
1496
1497 const QJniObject string = callMethod<jstring>("toString");
1498 return QtJniTypes::Detail::toQString(string.object<jstring>(), env);
1499}
1500
1501/*!
1502 \fn bool QJniObject::isClassAvailable(const char *className)
1503
1504 Returns true if the Java class \a className is available.
1505
1506 \code
1507 if (QJniObject::isClassAvailable("java/lang/String")) {
1508 // condition statement
1509 }
1510 \endcode
1511*/
1512bool QJniObject::isClassAvailable(const char *className)
1513{
1514 JNIEnv *env = QJniEnvironment::getJniEnv();
1515
1516 if (!env)
1517 return false;
1518
1519 return loadClass(className, env);
1520}
1521
1522/*!
1523 \fn bool QJniObject::isValid() const
1524
1525 Returns true if this instance holds a valid Java object.
1526
1527 \code
1528 QJniObject qjniObject; // ==> isValid() == false
1529 QJniObject qjniObject(0) // ==> isValid() == false
1530 QJniObject qjniObject("could/not/find/Class") // ==> isValid() == false
1531 \endcode
1532*/
1533bool QJniObject::isValid() const
1534{
1535 return d && d->m_jobject;
1536}
1537
1538/*!
1539 \fn QJniObject QJniObject::fromLocalRef(jobject localRef)
1540
1541 Creates a QJniObject from the local JNI reference \a localRef.
1542 This function takes ownership of \a localRef and frees it before returning.
1543
1544 \note Only call this function with a local JNI reference. For example, most raw JNI calls,
1545 through the JNI environment, return local references to a java object.
1546
1547 \code
1548 jobject localRef = env->GetObjectArrayElement(array, index);
1549 QJniObject element = QJniObject::fromLocalRef(localRef);
1550 \endcode
1551*/
1552QJniObject QJniObject::fromLocalRef(jobject lref)
1553{
1554 QJniObject obj(lref);
1555 obj.jniEnv()->DeleteLocalRef(lref);
1556 return obj;
1557}
1558
1559bool QJniObject::isSameObject(jobject obj) const
1560{
1561 if (!d)
1562 return obj == nullptr;
1563 if (d->m_jobject == obj)
1564 return true;
1565 if (!d->m_jobject || !obj)
1566 return false;
1567 return jniEnv()->IsSameObject(d->m_jobject, obj);
1568}
1569
1570bool QJniObject::isSameObject(const QJniObject &other) const
1571{
1572 if (!other.isValid())
1573 return !isValid();
1574 return isSameObject(other.d->m_jobject);
1575}
1576
1577void QJniObject::assign(jobject obj)
1578{
1579 if (d && isSameObject(obj))
1580 return;
1581
1582 d = QSharedPointer<QJniObjectPrivate>::create();
1583 if (obj) {
1584 JNIEnv *env = jniEnv();
1585 d->m_jobject = env->NewGlobalRef(obj);
1586 jclass objectClass = env->GetObjectClass(obj);
1587 d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
1588 env->DeleteLocalRef(objectClass);
1589 }
1590}
1591
1592jobject QJniObject::javaObject() const
1593{
1594 return d->m_jobject;
1595}
1596
1597/*!
1598 \class QtJniTypes::JObjectBase
1599 \brief The JObjectBase in the QtJniTypes namespace is the base of all declared Java types.
1600 \inmodule QtCore
1601 \internal
1602*/
1603
1604/*!
1605 \class QtJniTypes::JObject
1606 \inmodule QtCore
1607 \brief The JObject template in the QtJniTypes namespace is the base of declared Java types.
1608 \since Qt 6.8
1609
1610 JObject<Type> is a template class where \a Type specifies the Java type
1611 being represented.
1612
1613 This template gets specialized when using the Q_DECLARE_JNI_CLASS macro. The
1614 specialization produces a unique type in the QtJniTypes namespace. This
1615 allows the type system to deduce the correct signature in JNI calls when an
1616 instance of the specialized type is passed as a parameter.
1617
1618 Instances can be implicitly converted to and from QJniObject and jobject,
1619 and provide the same template API as QJniObject to call methods and access
1620 properties. Since instances of JObject know about the Java type they hold,
1621 APIs to access static methods or fields do not require the class name as an
1622 explicit parameter.
1623
1624 \sa Q_DECLARE_JNI_CLASS
1625*/
1626
1627/*!
1628 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject()
1629
1630 Default-constructs the JObject instance. This also default-constructs an
1631 instance of the represented Java type.
1632*/
1633
1634/*!
1635 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(const QJniObject &other)
1636
1637 Constructs a JObject instance that holds a reference to the same jobject as \a other.
1638*/
1639
1640/*!
1641 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(jobject other)
1642
1643 Constructs a JObject instance that holds a reference to \a other.
1644*/
1645
1646/*!
1647 \fn template <typename Type> QtJniTypes::JObject<Type>::JObject(QJniObject &&other)
1648
1649 Move-constructs a JObject instance from \a other.
1650*/
1651
1652/*!
1653 \fn template <typename Type> bool QtJniTypes::JObject<Type>::isValid() const
1654
1655 Returns whether the JObject instance holds a valid reference to a jobject.
1656
1657 \sa QJniObject::isValid()
1658*/
1659
1660/*!
1661 \fn template <typename Type> jclass QtJniTypes::JObject<Type>::objectClass() const
1662
1663 Returns the Java class that this JObject is an instance of as a jclass.
1664
1665 \sa className(), QJniObject::objectClass()
1666*/
1667
1668/*!
1669 \fn template <typename Type> QString QtJniTypes::JObject<Type>::toString() const
1670
1671 Returns a QString with a string representation of the Java object.
1672
1673 \sa QJniObject::toString()
1674*/
1675
1676/*!
1677 \fn template <typename Type> QByteArray QtJniTypes::JObject<Type>::className() const
1678
1679 Returns the name of the Java class that this object is an instance of.
1680
1681 \sa objectClass(), QJniObject::className()
1682*/
1683
1684/*!
1685 \fn template <typename Type> bool QtJniTypes::JObject<Type>::isClassAvailable()
1686
1687 Returns whether the class that this JObject specialization represents is
1688 available.
1689
1690 \sa QJniObject::isClassAvailable()
1691*/
1692
1693/*!
1694 \fn template <typename Type> JObject QtJniTypes::JObject<Type>::fromJObject(jobject object)
1695
1696 Constructs a JObject instance from \a object and returns that instance.
1697*/
1698
1699/*!
1700 \fn template <typename Type> template <typename ...Args> JObject QtJniTypes::JObject<Type>::construct(Args &&...args)
1701
1702 Constructs a Java object from \a args and returns a JObject instance that
1703 holds a reference to that Java object.
1704*/
1705
1706/*!
1707 \fn template <typename Type> JObject QtJniTypes::JObject<Type>::fromLocalRef(jobject ref)
1708
1709 Constructs a JObject that holds a local reference to \a ref, and returns
1710 that object.
1711*/
1712
1713/*!
1714 \fn template <typename Type> template <typename Ret, typename ...Args> auto QtJniTypes::JObject<Type>::callStaticMethod(const char *methodName, Args &&...args)
1715
1716 Calls the static method \a methodName with arguments \a args, and returns
1717 the result of type Ret (unless Ret is \c void). If Ret is a
1718 jobject type, then the returned value will be a QJniObject.
1719
1720 \sa QJniObject::callStaticMethod()
1721*/
1722
1723/*!
1724 \fn template <typename Type> bool QtJniTypes::JObject<Type>::registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
1725
1726 Registers the Java methods in \a methods with the Java class represented by
1727 the JObject specialization, and returns whether the registration was successful.
1728
1729 \sa QJniEnvironment::registerNativeMethods()
1730*/
1731
1732/*!
1733 \fn template <typename Type> template <typename T> auto QtJniTypes::JObject<Type>::getStaticField(const char *field)
1734
1735 Returns the value of the static field \a field.
1736
1737 \sa QJniObject::getStaticField()
1738*/
1739
1740/*!
1741 \fn template <typename Type> template <typename Ret, typename T> auto QtJniTypes::JObject<Type>::setStaticField(const char *field, T &&value)
1742
1743 Sets the static field \a field to \a value.
1744
1745 \sa QJniObject::setStaticField()
1746*/
1747
1748/*!
1749 \fn template <typename Type> template <typename Ret, typename ...Args> auto QtJniTypes::JObject<Type>::callMethod(const char *method, Args &&...args) const
1750
1751 Calls the instance method \a method with arguments \a args, and returns
1752 the result of type Ret (unless Ret is \c void). If Ret is a
1753 jobject type, then the returned value will be a QJniObject.
1754
1755 \sa QJniObject::callMethod()
1756*/
1757
1758/*!
1759 \fn template <typename Type> template <typename T> auto QtJniTypes::JObject<Type>::getField(const char *field) const
1760
1761 Returns the value of the instance field \a field.
1762
1763 \sa QJniObject::getField()
1764*/
1765
1766/*!
1767 \fn template <typename Type> template <typename Ret, typename T> auto QtJniTypes::JObject<Type>::setField(const char *field, T &&value)
1768
1769 Sets the value of the instance field \a field to \a value.
1770
1771 \sa QJniObject::setField()
1772*/
1773
1774
1775QT_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