Add compile-time generation of JNI class names
As with method signatures, register class names using template function specialization in the QtJniTypes namespace, and then declare C++ types as JNI classes with a class name string. Such classes implicitly get registered as JNI types as well. Add a QJniObject construct method (since C++ constructors that are templates cannot be explicitly instantiated with a type), and a QJniEnvironment::findClass overload. Add test coverage, also for the recently added macros for native methods. As a drive-by, change the name of the Q_JNI_DECLARE_NATIVE_METHOD macro to Q_DECLARE_JNI_NATIVE_METHOD for consistency. Change-Id: Ic19562d78da726f202b3bdf4e9354e8ad24d8bd9 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
parent
9614f4d434
commit
31f98957cf
@ -24,6 +24,8 @@ public:
|
||||
JNIEnv &operator*() const;
|
||||
JNIEnv *jniEnv() const;
|
||||
jclass findClass(const char *className);
|
||||
template<typename Class>
|
||||
jclass findClass() { return findClass(QtJniTypes::className<Class>().data()); }
|
||||
jmethodID findMethod(jclass clazz, const char *methodName, const char *signature);
|
||||
template<typename ...Args>
|
||||
jmethodID findMethod(jclass clazz, const char *methodName) {
|
||||
|
@ -45,6 +45,14 @@ public:
|
||||
inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {}
|
||||
~QJniObject();
|
||||
|
||||
template<typename Class, typename ...Args>
|
||||
static inline QJniObject construct(Args &&...args)
|
||||
{
|
||||
return QJniObject(QtJniTypes::className<Class>().data(),
|
||||
QtJniTypes::constructorSignature<Args...>().data(),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
jobject object() const;
|
||||
template <typename T> T object() const
|
||||
{
|
||||
|
@ -214,6 +214,21 @@ constexpr auto typeSignature()
|
||||
staticAssertTypeMismatch();
|
||||
}
|
||||
|
||||
template<bool flag = false>
|
||||
static void staticAssertClassNotRegistered()
|
||||
{
|
||||
static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto className()
|
||||
{
|
||||
if constexpr(std::is_same<T, jstring>::value)
|
||||
return String("java/lang/String");
|
||||
else
|
||||
staticAssertClassNotRegistered();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr bool isPrimitiveType()
|
||||
{
|
||||
@ -298,13 +313,17 @@ struct Object
|
||||
|
||||
} // namespace QtJniTypes
|
||||
|
||||
#define Q_DECLARE_JNI_TYPE(Type, Signature) \
|
||||
#define Q_DECLARE_JNI_TYPE_HELPER(Type) \
|
||||
namespace QtJniTypes { \
|
||||
struct Type : Object \
|
||||
{ \
|
||||
constexpr Type(jobject o) noexcept : Object{o} {} \
|
||||
}; \
|
||||
} \
|
||||
|
||||
|
||||
#define Q_DECLARE_JNI_TYPE(Type, Signature) \
|
||||
Q_DECLARE_JNI_TYPE_HELPER(Type) \
|
||||
template<> \
|
||||
constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
|
||||
{ \
|
||||
@ -315,7 +334,23 @@ constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
|
||||
return QtJniTypes::String(Signature); \
|
||||
} \
|
||||
|
||||
#define Q_JNI_DECLARE_NATIVE_METHOD(Method) \
|
||||
#define Q_DECLARE_JNI_CLASS(Type, Signature) \
|
||||
Q_DECLARE_JNI_TYPE_HELPER(Type) \
|
||||
template<> \
|
||||
constexpr auto QtJniTypes::className<QtJniTypes::Type>() \
|
||||
{ \
|
||||
return QtJniTypes::String(Signature); \
|
||||
} \
|
||||
template<> \
|
||||
constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
|
||||
{ \
|
||||
return QtJniTypes::String("L") \
|
||||
+ QtJniTypes::String(Signature) \
|
||||
+ QtJniTypes::String(";"); \
|
||||
} \
|
||||
|
||||
|
||||
#define Q_DECLARE_JNI_NATIVE_METHOD(Method) \
|
||||
namespace QtJniMethods { \
|
||||
static constexpr auto Method##_signature = \
|
||||
QtJniTypes::nativeMethodSignature(Method); \
|
||||
|
@ -65,6 +65,7 @@ void tst_QJniEnvironment::jniEnv()
|
||||
// try to find an existing class with QJniEnvironment
|
||||
QJniEnvironment env;
|
||||
QVERIFY(env.findClass("java/lang/Object"));
|
||||
QVERIFY(env.findClass<jstring>());
|
||||
|
||||
// try to find a nonexistent class
|
||||
QVERIFY(!env.findClass("this/doesnt/Exist"));
|
||||
@ -98,14 +99,14 @@ static void callbackFromJava(JNIEnv *env, jobject /*thiz*/, jstring value)
|
||||
Q_UNUSED(env)
|
||||
registerNativesString = QJniObject(value).toString();
|
||||
}
|
||||
Q_JNI_DECLARE_NATIVE_METHOD(callbackFromJava);
|
||||
Q_DECLARE_JNI_NATIVE_METHOD(callbackFromJava);
|
||||
|
||||
static void callbackFromJavaNoCtor(JNIEnv *env, jobject /*thiz*/, jstring value)
|
||||
{
|
||||
Q_UNUSED(env)
|
||||
registerNativesString = QJniObject(value).toString();
|
||||
}
|
||||
Q_JNI_DECLARE_NATIVE_METHOD(callbackFromJavaNoCtor);
|
||||
Q_DECLARE_JNI_NATIVE_METHOD(callbackFromJavaNoCtor);
|
||||
|
||||
void tst_QJniEnvironment::registerNativeMethods()
|
||||
{
|
||||
@ -145,7 +146,7 @@ static void intCallbackFromJava(JNIEnv *env, jobject /*thiz*/, jint value)
|
||||
Q_UNUSED(env)
|
||||
registerNativeInteger = static_cast<int>(value);
|
||||
}
|
||||
Q_JNI_DECLARE_NATIVE_METHOD(intCallbackFromJava);
|
||||
Q_DECLARE_JNI_NATIVE_METHOD(intCallbackFromJava);
|
||||
|
||||
void tst_QJniEnvironment::registerNativeMethodsByJclass()
|
||||
{
|
||||
|
@ -134,6 +134,11 @@ void tst_QJniObject::ctor()
|
||||
QVERIFY(object.isValid());
|
||||
}
|
||||
|
||||
{
|
||||
QJniObject object = QJniObject::construct<jstring>();
|
||||
QVERIFY(object.isValid());
|
||||
}
|
||||
|
||||
{
|
||||
QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java"));
|
||||
QJniObject object("java/lang/String", "(Ljava/lang/String;)V", string.object<jstring>());
|
||||
@ -143,7 +148,7 @@ void tst_QJniObject::ctor()
|
||||
|
||||
{
|
||||
QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java"));
|
||||
QJniObject object("java/lang/String", string.object<jstring>());
|
||||
QJniObject object = QJniObject::construct<jstring>(string.object<jstring>());
|
||||
QVERIFY(object.isValid());
|
||||
QCOMPARE(string.toString(), object.toString());
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void nativeMethod();
|
||||
};
|
||||
|
||||
struct QtJavaWrapper {};
|
||||
@ -45,6 +46,11 @@ static_assert(QtJniTypes::typeSignature<QtJniTypes::JavaType>() == "Lorg/qtproje
|
||||
Q_DECLARE_JNI_TYPE(ArrayType, "[Lorg/qtproject/qt/ArrayType;")
|
||||
static_assert(QtJniTypes::typeSignature<QtJniTypes::ArrayType>() == "[Lorg/qtproject/qt/ArrayType;");
|
||||
|
||||
static_assert(QtJniTypes::className<jstring>() == "java/lang/String");
|
||||
|
||||
Q_DECLARE_JNI_CLASS(QtTextToSpeech, "org/qtproject/qt/android/speech/QtTextToSpeech")
|
||||
static_assert(QtJniTypes::className<QtJniTypes::QtTextToSpeech>() == "org/qtproject/qt/android/speech/QtTextToSpeech");
|
||||
|
||||
static_assert(QtJniTypes::fieldSignature<jint>() == "I");
|
||||
static_assert(QtJniTypes::fieldSignature<jint>() != "X");
|
||||
static_assert(QtJniTypes::fieldSignature<jint>() != "Ljava/lang/Object;");
|
||||
@ -97,6 +103,22 @@ void tst_QJniTypes::initTestCase()
|
||||
|
||||
}
|
||||
|
||||
static bool nativeFunction(JNIEnv *, jclass, int, jstring, long)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Q_DECLARE_JNI_NATIVE_METHOD(nativeFunction)
|
||||
|
||||
static_assert(QtJniTypes::nativeMethodSignature(nativeFunction) == "(ILjava/lang/String;J)Z");
|
||||
|
||||
void tst_QJniTypes::nativeMethod()
|
||||
{
|
||||
const auto method = Q_JNI_NATIVE_METHOD(nativeFunction);
|
||||
QVERIFY(method.fnPtr == nativeFunction);
|
||||
QCOMPARE(method.name, "nativeFunction");
|
||||
QCOMPARE(method.signature, "(ILjava/lang/String;J)Z");
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QJniTypes)
|
||||
|
||||
#include "tst_qjnitypes.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user