summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2022-04-26 13:19:46 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2022-05-17 00:34:06 +0200
commit367092d7e02bfe0054b78e856b7addbdb56aae2e (patch)
treef17793c691da879973689d9cab355ea3f2f1e4e1
parentf6e89e901ba718d64b50f79f3b1b327a7f6a0211 (diff)
Return specific types for frequently used Java objects
This allows us to specialize JNI type signature templates for e.g. the context object, which in Java signatures is "android/content/Context". Introduce a Q_DECLARE_JNI_TYPE macro that takes care of the plumbing. The types declared this way live in the QtJniTypes namespace, and transparently convert from and to jobject. Since jobject is a typedef to _jobject* we cannot create a subclass. Use a "Object" superclass that we can provide a QJniObject constructor for so that we don't require the QJniObject declaration to be able to use the macro. The APIs in the QNativeInterface namespace doesn't provide source or binary compatibility guarantees, so we can change the return types. Change-Id: I4cf9fa734ec9a5550b6fddeb14ef0ffd72663f29 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h9
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp6
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h10
-rw-r--r--src/corelib/kernel/qjniobject.h1
-rw-r--r--src/corelib/kernel/qjnitypes.h30
-rw-r--r--src/corelib/platform/android/qandroidnativeinterface.cpp4
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp8
-rw-r--r--src/plugins/platforms/android/androidjnimain.h5
-rw-r--r--tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp9
9 files changed, 67 insertions, 15 deletions
diff --git a/src/corelib/kernel/qcoreapplication_platform.h b/src/corelib/kernel/qcoreapplication_platform.h
index 9807cb7817..fe0fee915b 100644
--- a/src/corelib/kernel/qcoreapplication_platform.h
+++ b/src/corelib/kernel/qcoreapplication_platform.h
@@ -18,6 +18,7 @@
#include <QtCore/qcoreapplication.h>
#if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
+#include <QtCore/qjnitypes.h>
#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
#include <QtCore/qfuture.h>
#include <QtCore/qvariant.h>
@@ -31,13 +32,21 @@ typedef _jobject* jobject;
QT_BEGIN_NAMESPACE
+#if defined(Q_OS_ANDROID)
+Q_DECLARE_JNI_TYPE(Context, "Landroid/content/Context;")
+#endif
+
namespace QNativeInterface
{
#if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
struct Q_CORE_EXPORT QAndroidApplication
{
QT_DECLARE_NATIVE_INTERFACE(QAndroidApplication, 1, QCoreApplication)
+#ifdef Q_CLANG_QDOC
static jobject context();
+#else
+ static QtJniTypes::Context context();
+#endif
static bool isActivityContext();
static int sdkVersion();
static void hideSplashScreen(int duration = 0);
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 6954ed1193..ec6a7a58a3 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -289,18 +289,18 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
return JNI_OK;
}
-jobject QtAndroidPrivate::activity()
+QtJniTypes::Activity QtAndroidPrivate::activity()
{
QReadLocker locker(g_updateMutex());
return g_jActivity;
}
-jobject QtAndroidPrivate::service()
+QtJniTypes::Service QtAndroidPrivate::service()
{
return g_jService;
}
-jobject QtAndroidPrivate::context()
+QtJniTypes::Context QtAndroidPrivate::context()
{
QReadLocker locker(g_updateMutex());
if (g_jActivity)
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index b0f80204e9..bce2b782de 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -18,9 +18,13 @@
#include <jni.h>
#include <functional>
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qcoreapplication_platform.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_TYPE(Activity, "Landroid/app/Activity;")
+Q_DECLARE_JNI_TYPE(Service, "Landroid/app/Service;")
+
namespace QtAndroidPrivate
{
class Q_CORE_EXPORT ActivityResultListener
@@ -66,9 +70,9 @@ namespace QtAndroidPrivate
virtual jobject onBind(jobject intent) = 0;
};
- Q_CORE_EXPORT jobject activity();
- Q_CORE_EXPORT jobject service();
- Q_CORE_EXPORT jobject context();
+ Q_CORE_EXPORT QtJniTypes::Activity activity();
+ Q_CORE_EXPORT QtJniTypes::Service service();
+ Q_CORE_EXPORT QtJniTypes::Context context();
Q_CORE_EXPORT JavaVM *javaVM();
Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env);
Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
index 0d5c95b405..bbaa6ee70c 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -42,6 +42,7 @@ public:
std::forward<Args>(args)...)
{}
QJniObject(jobject globalRef);
+ inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {}
~QJniObject();
jobject object() const;
diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h
index 3d723a8782..e5306f3c54 100644
--- a/src/corelib/kernel/qjnitypes.h
+++ b/src/corelib/kernel/qjnitypes.h
@@ -227,7 +227,8 @@ static constexpr bool isObjectType()
return true;
} else {
constexpr auto signature = typeSignature<T>();
- return signature.startsWith('L') && signature.endsWith(';');
+ return (signature.startsWith('L') || signature.startsWith('['))
+ && signature.endsWith(';');
}
}
@@ -273,8 +274,35 @@ static constexpr auto constructorSignature()
return methodSignature<void, Args...>();
}
+// A generic thin wrapper around jobject, convertible to jobject.
+// We need this as a baseclass so that QJniObject can be implicitly
+// constructed from the various subclasses - we can't provide an
+// operator QJniObject() here as the class is not declared.
+struct Object
+{
+ jobject _object;
+ constexpr operator jobject() const { return _object; }
+};
+
} // namespace QtJniTypes
+#define Q_DECLARE_JNI_TYPE(Type, Signature) \
+namespace QtJniTypes { \
+struct Type : Object \
+{ \
+ constexpr Type(jobject o) noexcept : Object{o} {} \
+}; \
+} \
+template<> \
+constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
+{ \
+ static_assert((Signature[0] == 'L' || Signature[0] == '[') \
+ && Signature[sizeof(Signature) - 2] == ';', \
+ "Type signature needs to start with 'L' or '['" \
+ " and end with ';'"); \
+ return QtJniTypes::String(Signature); \
+} \
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp
index 09c24df410..a93844139b 100644
--- a/src/corelib/platform/android/qandroidnativeinterface.cpp
+++ b/src/corelib/platform/android/qandroidnativeinterface.cpp
@@ -38,14 +38,14 @@ static QBasicMutex g_pendingRunnablesMutex;
QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication);
/*!
- \fn jobject QNativeInterface::QAndroidApplication::context()
+ \fn jobject QNativeInterface::QAndroidApplication::context()
Returns the Android context as a \c jobject. The context is an \c Activity
if the main activity object is valid. Otherwise, the context is a \c Service.
\since 6.2
*/
-jobject QNativeInterface::QAndroidApplication::context()
+QtJniTypes::Context QNativeInterface::QAndroidApplication::context()
{
return QtAndroidPrivate::context();
}
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 0f417cdb23..3b5e656630 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -43,9 +43,9 @@ static jmethodID m_loadClassMethodID = nullptr;
static AAssetManager *m_assetManager = nullptr;
static jobject m_assets = nullptr;
static jobject m_resourcesObj = nullptr;
-static jobject m_activityObject = nullptr;
+static QtJniTypes::Activity m_activityObject = nullptr;
static jmethodID m_createSurfaceMethodID = nullptr;
-static jobject m_serviceObject = nullptr;
+static QtJniTypes::Service m_serviceObject = nullptr;
static jmethodID m_setSurfaceGeometryMethodID = nullptr;
static jmethodID m_destroySurfaceMethodID = nullptr;
@@ -159,12 +159,12 @@ namespace QtAndroid
return m_applicationClass;
}
- jobject activity()
+ QtJniTypes::Activity activity()
{
return m_activityObject;
}
- jobject service()
+ QtJniTypes::Service service()
{
return m_serviceObject;
}
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index 1425d2173c..8d05e31f66 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -11,6 +11,7 @@
#include <android/asset_manager.h>
#include <QImage>
+#include <private/qjnihelpers_p.h>
QT_BEGIN_NAMESPACE
@@ -49,8 +50,8 @@ namespace QtAndroid
jobject assets();
AAssetManager *assetManager();
jclass applicationClass();
- jobject activity();
- jobject service();
+ QtJniTypes::Activity activity();
+ QtJniTypes::Service service();
// Keep synchronized with flags in ActivityDelegate.java
enum SystemUiVisibility {
diff --git a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
index 49835a0e45..c4abfe399a 100644
--- a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
+++ b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
@@ -40,6 +40,11 @@ static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() == "Lorg/qtproject/qt/a
static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() != "Ljava/lang/Object;");
static_assert(!(QtJniTypes::typeSignature<QtJavaWrapper>() == "X"));
+Q_DECLARE_JNI_TYPE(JavaType, "Lorg/qtproject/qt/JavaType;");
+static_assert(QtJniTypes::typeSignature<QtJniTypes::JavaType>() == "Lorg/qtproject/qt/JavaType;");
+Q_DECLARE_JNI_TYPE(ArrayType, "[Lorg/qtproject/qt/ArrayType;")
+static_assert(QtJniTypes::typeSignature<QtJniTypes::ArrayType>() == "[Lorg/qtproject/qt/ArrayType;");
+
static_assert(QtJniTypes::fieldSignature<jint>() == "I");
static_assert(QtJniTypes::fieldSignature<jint>() != "X");
static_assert(QtJniTypes::fieldSignature<jint>() != "Ljava/lang/Object;");
@@ -57,6 +62,8 @@ static_assert(QtJniTypes::methodSignature<void, jint>() == "(I)V");
static_assert(QtJniTypes::methodSignature<void, jint, jstring>() == "(ILjava/lang/String;)V");
static_assert(QtJniTypes::methodSignature<jlong, jint, jclass>() == "(ILjava/lang/Class;)J");
static_assert(QtJniTypes::methodSignature<jobject, jint, jstring>() == "(ILjava/lang/String;)Ljava/lang/Object;");
+static_assert(QtJniTypes::methodSignature<QtJniTypes::JavaType, jint, jstring>()
+ == "(ILjava/lang/String;)Lorg/qtproject/qt/JavaType;");
static_assert(QtJniTypes::isPrimitiveType<jint>());
static_assert(QtJniTypes::isPrimitiveType<void>());
@@ -66,6 +73,7 @@ static_assert(!QtJniTypes::isPrimitiveType<QtCustomJniObject>());
static_assert(!QtJniTypes::isObjectType<jint>());
static_assert(!QtJniTypes::isObjectType<void>());
static_assert(QtJniTypes::isObjectType<jobject>());
+static_assert(QtJniTypes::isObjectType<jobjectArray>());
static_assert(QtJniTypes::isObjectType<QtCustomJniObject>());
static_assert(QtJniTypes::String("ABCDE").startsWith("ABC"));
@@ -86,6 +94,7 @@ static_assert(!QtJniTypes::String("ABCDE").endsWith('F'));
void tst_QJniTypes::initTestCase()
{
+
}
QTEST_MAIN(tst_QJniTypes)