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
qandroidnativeinterface.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 <QtCore/qcoreapplication_platform.h>
6
7#include <QtCore/private/qnativeinterface_p.h>
8#include <QtCore/private/qjnihelpers_p.h>
9#include <QtCore/qjniobject.h>
10#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
11#include <QtCore/qfuture.h>
12#include <QtCore/qfuturewatcher.h>
13#include <QtCore/qpromise.h>
14#include <QtCore/qtimer.h>
15#include <QtCore/qthreadpool.h>
16#include <deque>
17#include <memory>
18#endif
19
21
22#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
23static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative";
24
25struct PendingRunnable {
28};
29
33#endif
34
35/*!
36 \class QNativeInterface::QAndroidApplication
37 \since 6.2
38 \brief Native interface to a core application on Android.
39
40 Accessed through QCoreApplication::nativeInterface().
41
42 \inmodule QtCore
43 \inheaderfile QCoreApplication
44 \ingroup native-interfaces
45 \ingroup native-interfaces-qcoreapplication
46*/
48
49/*!
50 \fn QJniObject QNativeInterface::QAndroidApplication::context()
51
52 Returns the Android context as a \c QtJniTypes::Context. The context
53 is an \c Activity if the most recently started activity object is valid.
54 Otherwise, the context is a \c Service.
55
56 \since 6.2
57*/
58QtJniTypes::Context QNativeInterface::QAndroidApplication::context()
59{
60 return QtAndroidPrivate::context();
61}
62
63/*!
64 \fn bool QNativeInterface::QAndroidApplication::isActivityContext()
65
66 Returns \c true if QAndroidApplication::context() provides an \c Activity
67 context.
68
69 \since 6.2
70*/
71bool QNativeInterface::QAndroidApplication::isActivityContext()
72{
73 return QtAndroidPrivate::activity().isValid();
74}
75
76/*!
77 \fn int QNativeInterface::QAndroidApplication::sdkVersion()
78
79 Returns the Android SDK version. This is also known as the API level.
80
81 \since 6.2
82*/
83int QNativeInterface::QAndroidApplication::sdkVersion()
84{
85 return QtAndroidPrivate::androidSdkVersion();
86}
87
88/*!
89 \fn void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
90
91 Hides the splash screen by using a fade effect for the given \a duration.
92 If \a duration is not provided (default is 0) the splash screen is hidden
93 immedetiately after the app starts.
94
95 \since 6.2
96*/
97void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
98{
99 QtAndroidPrivate::activity().callMethod<void>("hideSplashScreen", duration);
100}
101
102/*!
103 Posts the function \a runnable to the Android thread. The function will be
104 queued and executed on the Android UI thread. If the call is made on the
105 Android UI thread \a runnable will be executed immediately. If the Android
106 app is paused or the main Activity is null, \c runnable is added to the
107 Android main thread's queue.
108
109 This call returns a QFuture<QVariant> which allows doing both synchronous
110 and asynchronous calls, and can handle any return type. However, to get
111 a result back from the QFuture::result(), QVariant::value() should be used.
112
113 If the \a runnable execution takes longer than the period of \a timeout,
114 the blocking calls \l QFuture::waitForFinished() and \l QFuture::result()
115 are ended once \a timeout has elapsed. However, if \a runnable has already
116 started execution, it won't be cancelled.
117
118 The following example shows how to run an asynchronous call that expects
119 a return type:
120
121 \code
122 auto task = QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
123 QJniObject surfaceView;
124 if (!surfaceView.isValid())
125 qDebug() << "SurfaceView object is not valid yet";
126
127 surfaceView = QJniObject("android/view/SurfaceView",
128 "(Landroid/content/Context;)V",
129 QNativeInterface::QAndroidApplication::context());
130
131 return QVariant::fromValue(surfaceView);
132 }).then([](QFuture<QVariant> future) {
133 auto surfaceView = future.result().value<QJniObject>();
134 if (surfaceView.isValid())
135 qDebug() << "Retrieved SurfaceView object is valid";
136 });
137 \endcode
138
139 The following example shows how to run a synchronous call with a void
140 return type:
141
142 \code
143 QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
144 QJniObject activity = QNativeInterface::QAndroidApplication::context();
145 // Hide system ui elements or go full screen
146 activity.callObjectMethod("getWindow", "()Landroid/view/Window;")
147 .callObjectMethod("getDecorView", "()Landroid/view/View;")
148 .callMethod<void>("setSystemUiVisibility", "(I)V", 0xffffffff);
149 }).waitForFinished();
150 \endcode
151
152 \note Becareful about the type of operations you do on the Android's main
153 thread, as any long operation can block the app's UI rendering and input
154 handling. If the function is expected to have long execution time, it's
155 also good to use a \l QDeadlineTimer in your \a runnable to manage
156 the execution and make sure it doesn't block the UI thread. Usually,
157 any operation longer than 5 seconds might block the app's UI. For more
158 information, see \l {Android: Keep your app responsive}{Keep your app responsive}.
159
160 \since 6.2
161*/
162#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
163QFuture<QVariant> QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
164 const std::function<QVariant()> &runnable,
165 const QDeadlineTimer timeout)
166{
167 auto promise = std::make_shared<QPromise<QVariant>>();
168 QFuture<QVariant> future = promise->future();
169 promise->start();
170
171 if (!timeout.isForever()) {
172 QThreadPool::globalInstance()->start([=]() mutable {
173 QEventLoop loop;
174 QTimer::singleShot(timeout.remainingTime(), &loop, [&]() {
175 future.cancel();
176 promise->finish();
177 loop.quit();
178 });
179
180 QFutureWatcher<QVariant> watcher;
181 QObject::connect(&watcher, &QFutureWatcher<QVariant>::finished, &loop, [&]() {
182 loop.quit();
183 });
184 QObject::connect(&watcher, &QFutureWatcher<QVariant>::canceled, &loop, [&]() {
185 loop.quit();
186 });
187 watcher.setFuture(future);
188
189 // we're going to sleep, make sure we don't block
190 // QThreadPool::globalInstance():
191
192 QThreadPool::globalInstance()->releaseThread();
193 const auto sg = qScopeGuard([] {
194 QThreadPool::globalInstance()->reserveThread();
195 });
196 loop.exec();
197 });
198 }
199
200 QMutexLocker locker(&g_pendingRunnablesMutex);
201#ifdef __cpp_aggregate_paren_init
202 g_pendingRunnables->emplace_back(runnable, std::move(promise));
203#else
204 g_pendingRunnables->push_back({runnable, std::move(promise)});
205#endif
206 locker.unlock();
207
208 QJniObject::callStaticMethod<void>(qtNativeClassName,
209 "runPendingCppRunnablesOnAndroidThread",
210 "()V");
211 return future;
212}
213
214// function called from Java from Android UI thread
215static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
216{
217 // run all posted runnables
218 for (;;) {
219 QMutexLocker locker(&g_pendingRunnablesMutex);
220 if (g_pendingRunnables->empty())
221 break;
222
223 PendingRunnable r = std::move(g_pendingRunnables->front());
224 g_pendingRunnables->pop_front();
225 locker.unlock();
226
227 // run the runnable outside the sync block!
228 if (!r.promise->isCanceled())
229 r.promise->addResult(r.function());
230 r.promise->finish();
231 }
232}
233#endif
234
235bool QtAndroidPrivate::registerNativeInterfaceNatives(QJniEnvironment &env)
236{
237#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
238 const JNINativeMethod methods = {"runPendingCppRunnables", "()V", (void *)runPendingCppRunnables};
239 return env.registerNativeMethods(qtNativeClassName, &methods, 1);
240#else
241 return true;
242#endif
243}
244
245QT_END_NAMESPACE
Native interface to a core application on Android.