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