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
androidbackendregister.h
Go to the documentation of this file.
1// Copyright (C) 2024 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#ifndef ANDROIDBACKENDREGISTER_H
6#define ANDROIDBACKENDREGISTER_H
7
8#include <type_traits>
9
10#include <QtCore/qjnienvironment.h>
11#include <QtCore/qjnitypes.h>
12#include <QtCore/qjniobject.h>
13
14#include <QtCore/qstring.h>
15#include <QtCore/qmap.h>
16#include <QtCore/qmutex.h>
17#include <QtCore/qloggingcategory.h>
18
19QT_BEGIN_NAMESPACE
20
21Q_DECLARE_LOGGING_CATEGORY(lcAndroidBackendRegister)
22
23/*
24 \internal
25
26 This class is used to [un]register QJniObjects which implement specific interfaces. These
27 objects can then be fetched or directly called by using that interface.
28
29 This is intended to decouple the Qt C++ code from the underlying Java implementation, as Qt now
30 has multiple separate usecases, each of which may have different implementations and support
31 different features.
32
33 To use this register, the interface must be declared as a JNI class via Q_DECLARE_JNI_CLASS:
34
35 Q_DECLARE_JNI_CLASS(ImaginaryInterface, "org/qtproject/qt/android/ImaginaryInterface")
36
37 Where ImaginaryInterface is a Java interface like this:
38
39 @UsedFromNativeCode
40 interface ImaginaryInterface {
41 void doSomething(int imaginary, int imaginary2);
42 }
43
44 After that, the features provided by that interface can be used in the C++ code in two ways:
45
46 Use the convenience method callInterface() to call a method directly:
47
48 AndroidBackendRegister *reg = QtAndroid::backendRegister();
49 int imaginary, imaginary2;
50 reg->callInterface<QtJniTypes::ImaginaryInterface, void>("doSomething", imaginary, imaginary2);
51
52 Or get the QJniObject directly and use it as you would any other QJniObject:
53 AndroidBackendRegister *reg = QtAndroid::backendRegister();
54 auto imaginary = reg->getInterface<QtJniTypes::ImaginaryInterface>();
55 // ... do whatever with QJniObject
56
57 In order to register a new interface on the Java side, the BackendRegister class must be used,
58 with its native functions registerBackend() and unregisterBackend():
59
60 BackendRegister.registerBackend(ImaginaryInterface.class, imaginaryInterfaceObject);
61
62 and
63
64 BackendRegister.unregisterBackend(ImaginaryInterface.class);
65
66 Note that only one object can be registered for each interface. If multiple objects are
67 registered, only the latest one is kept. Thus, you only need to declare the interface you want
68 to unregister, not the object that implements the interface as well.
69
70 If the interface needs to be available as soon as possible, it should be registered immediately
71 after Qt has started, by using the QtNative app state listener functionality.
72*/
73
74template <typename T>
75using ValidInterfaceType = std::enable_if_t<std::is_base_of_v<QtJniTypes::JObjectBase, T>, bool>;
76
78{
79public:
80 static bool registerNatives();
81
82 /*
83 \internal
84
85 Returns a QJniObject which is registered for the given interface.
86 Requires the type of the requested interface to be registered via
87 Q_DECLARE_JNI_CLASS. (see ValidInterfaceType).
88 */
89 template <typename T, ValidInterfaceType<T> = true>
90 [[nodiscard]] T getInterface()
91 {
92 QMutexLocker lock(&m_registerMutex);
93 return m_register.value(QString(QtJniTypes::Traits<T>::className().data()));
94 }
95
96 template <typename Object>
100
101 /*
102 \internal
103
104 Convenience function that calls getInterface<Interface>() and then QJniObject::callMethod()
105 on the resulting object, forwarding the rest of the parameters to that function.
106
107 If the interface is not registered, a warning is printed and an empty object is returned.
108 */
109 template <typename Interface, typename Ret = void, typename... Args,
110 ValidInterfaceType<Interface> = true>
111 auto callInterface(const char *func, Args... args)
112 {
113 if (const auto obj = getInterface<Interface>(); obj.isValid()) {
114 return obj.template callMethod<Ret, Args...>(func, std::forward<Args>(args)...);
115 } else {
116 qWarning() << "No interface with className"
117 << QtJniTypes::Traits<Interface>::className() << "has been registered.";
118 }
119
120 if constexpr (IsObjectType<Ret>::value)
121 return Ret(QJniObject());
122 if constexpr (!std::is_same_v<Ret, void>)
123 return Ret{};
124 }
125
126private:
127 QMutex m_registerMutex;
128 QMap<QString, QJniObject> m_register;
129
130 static jboolean isNull(JNIEnv *, jclass);
133 Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(registerBackend)
135 Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(unregisterBackend)
136};
137
138QT_END_NAMESPACE
139
140#endif // ANDROIDBACKENDREGISTER_H
auto callInterface(const char *func, Args... args)
Combined button and popup list for selecting options.
AndroidBackendRegister * backendRegister()