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