diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2021-04-30 11:49:08 +0200 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2021-05-03 18:09:23 +0200 |
commit | 34f72ca52e7312172f75c1f79da0d226d2d4583b (patch) | |
tree | f2aa824582a96d931b07220d11904652fd9c88a5 | |
parent | 96d9cf8de40804690b98d06daf15f2067b527d32 (diff) |
QJniEnvironment: extend API
This patch adds some convenience methods to QJniEnvironment API:
* an overload of registerNativeMethods() that accepts jclass instead
of const char *className.
* a findMethod() function is added to query a methodID of a static
or nonstatic method by its name and signature.
Task-number: QTBUG-92952
Change-Id: Ib1bc892decea97e625c4822888b6183af6edd6dc
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
4 files changed, 147 insertions, 5 deletions
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp index f7a5fcb686..0e6cafcd91 100644 --- a/src/corelib/kernel/qjnienvironment.cpp +++ b/src/corelib/kernel/qjnienvironment.cpp @@ -198,6 +198,54 @@ jclass QJniEnvironment::findClass(const char *className) } /*! + Searches for an instance method of a class \a clazz. The method is specified + by its \a methodName and \a signature. + + Returns the method ID or \c nullptr if the method is not found. + + A usecase for this method is searching for class methods and caching their + IDs, so that they could later be used for calling the methods. +*/ +jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, const char *signature) +{ + jmethodID id = d->jniEnv->GetMethodID(clazz, methodName, signature); + if (checkAndClearExceptions(d->jniEnv)) + return nullptr; + + return id; +} + +/*! + Searches for a static method of a class \a clazz. The method is specified + by its \a methodName and \a signature. + + Returns the method ID or \c nullptr if the method is not found. + + A usecase for this method is searching for class methods and caching their + IDs, so that they could later be used for calling the methods. + + \code + QJniEnvironment env; + jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass"); + jmethodID methodId = env.findStaticMethod(javaClass, + "staticJavaMethod", + "(Ljava/lang/String;)V"); + QJniObject javaMessage = QJniObject::fromString("findStaticMethod example"); + QJniObject::callStaticMethod<void>(javaClass, + methodId, + javaMessage.object<jstring>()); + \endcode +*/ +jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName, const char *signature) +{ + jmethodID id = d->jniEnv->GetStaticMethodID(clazz, methodName, signature); + if (checkAndClearExceptions(d->jniEnv)) + return nullptr; + + return id; +} + +/*! \fn JavaVM *QJniEnvironment::javaVM() Returns the Java VM interface for the current process. Although it might @@ -216,7 +264,7 @@ JavaVM *QJniEnvironment::javaVM() which can call native C++ functions from class \a className. These methods must be registered before any attempt to call them. - Returns True if the registration is successful, otherwise False. + Returns \c true if the registration is successful, otherwise \c false. Each element in the methods array consists of: \list @@ -240,14 +288,31 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth return false; jclass clazz = d->jniEnv->GetObjectClass(classObject.object()); + const bool result = registerNativeMethods(clazz, methods, size); + d->jniEnv->DeleteLocalRef(clazz); + + return result; +} + +/*! + \overload + + This overload uses a previously cached jclass instance \a clazz. + + \code + JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)}, + {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}}; + QJniEnvironment env; + jclass clazz = env.findClass("org/qtproject/android/TestJavaClass"); + env.registerNativeMethods(clazz, methods, 2); + \endcode +*/ +bool QJniEnvironment::registerNativeMethods(jclass clazz, JNINativeMethod methods[], int size) +{ if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) { checkAndClearExceptions(); - d->jniEnv->DeleteLocalRef(clazz); return false; } - - d->jniEnv->DeleteLocalRef(clazz); - return true; } diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h index 69a24cbe18..2053e0a50f 100644 --- a/src/corelib/kernel/qjnienvironment.h +++ b/src/corelib/kernel/qjnienvironment.h @@ -58,8 +58,11 @@ public: JNIEnv &operator*() const; JNIEnv *jniEnv() const; jclass findClass(const char *className); + jmethodID findMethod(jclass clazz, const char *methodName, const char *signature); + jmethodID findStaticMethod(jclass clazz, const char *methodName, const char *signature); static JavaVM *javaVM(); bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size); + bool registerNativeMethods(jclass clazz, JNINativeMethod methods[], int size); enum class OutputMode { Silent, diff --git a/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java b/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java index d6c5be45cb..8f36dcc5fd 100644 --- a/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java +++ b/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java @@ -31,11 +31,16 @@ package org.qtproject.qt.android.testdatapackage; public class QtJniEnvironmentTestClass { private static native void callbackFromJava(String message); + private static native void intCallbackFromJava(int value); public static void appendJavaToString(String message) { callbackFromJava("From Java: " + message); } + public static void convertToInt(String message) + { + intCallbackFromJava(Integer.parseInt(message)); + } } diff --git a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp index 278554e496..41192e1f4b 100644 --- a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp +++ b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp @@ -36,6 +36,7 @@ static const char javaTestClass[] = "org/qtproject/qt/android/testdatapackage/QtJniEnvironmentTestClass"; static QString registerNativesString = QStringLiteral("Qt"); +static int registerNativeInteger = 0; class tst_QJniEnvironment : public QObject { @@ -45,6 +46,9 @@ private slots: void jniEnv(); void javaVM(); void registerNativeMethods(); + void registerNativeMethodsByJclass(); + void findMethod(); + void findStaticMethod(); }; void tst_QJniEnvironment::jniEnv() @@ -134,6 +138,71 @@ void tst_QJniEnvironment::registerNativeMethods() QVERIFY(registerNativesString == QStringLiteral("From Java: Qt")); } +static void intCallbackFromJava(JNIEnv *env, jobject /*thiz*/, jint value) +{ + Q_UNUSED(env) + registerNativeInteger = static_cast<int>(value); +} + +void tst_QJniEnvironment::registerNativeMethodsByJclass() +{ + JNINativeMethod methods[] { + { "intCallbackFromJava", "(I)V", reinterpret_cast<void *>(intCallbackFromJava) } + }; + + QJniEnvironment env; + jclass clazz = env.findClass(javaTestClass); + QVERIFY(clazz != 0); + QVERIFY(env.registerNativeMethods(clazz, methods, 1)); + + QCOMPARE(registerNativeInteger, 0); + + QJniObject parameter = QJniObject::fromString(QString("123")); + QJniObject::callStaticMethod<void>(clazz, "convertToInt", "(Ljava/lang/String;)V", + parameter.object<jstring>()); + QTest::qWait(200); + QCOMPARE(registerNativeInteger, 123); +} + +void tst_QJniEnvironment::findMethod() +{ + QJniEnvironment env; + jclass clazz = env.findClass("java/lang/Integer"); + QVERIFY(clazz != nullptr); + + // existing method + jmethodID methodId = env.findMethod(clazz, "toString", "()Ljava/lang/String;"); + QVERIFY(methodId != nullptr); + + // invalid signature + jmethodID invalid = env.findMethod(clazz, "unknown", "()I"); + QVERIFY(invalid == nullptr); + // check that all exceptions are already cleared + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniEnvironment::findStaticMethod() +{ + QJniEnvironment env; + jclass clazz = env.findClass("java/lang/Integer"); + QVERIFY(clazz != nullptr); + + // existing method + jmethodID staticMethodId = env.findStaticMethod(clazz, "parseInt", "(Ljava/lang/String;)I"); + QVERIFY(staticMethodId != nullptr); + + QJniObject parameter = QJniObject::fromString("123"); + jint result = QJniObject::callStaticMethod<jint>(clazz, staticMethodId, + parameter.object<jstring>()); + QCOMPARE(result, 123); + + // invalid method + jmethodID invalid = env.findStaticMethod(clazz, "unknown", "()I"); + QVERIFY(invalid == nullptr); + // check that all exceptions are already cleared + QVERIFY(!env.checkAndClearExceptions()); +} + QTEST_MAIN(tst_QJniEnvironment) #include "tst_qjnienvironment.moc" |