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