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