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 @@ -197,6 +197,54 @@ jclass QJniEnvironment::findClass(const char *className) return QtAndroidPrivate::findClass(className, d->jniEnv); } +/*! + 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(javaClass, + methodId, + javaMessage.object()); + \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() @@ -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()); - if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) { - checkAndClearExceptions(); - d->jniEnv->DeleteLocalRef(clazz); - return false; - } - + 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(fromJavaOne)}, + {"callNativeTwo", "(I)V", reinterpret_cast(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(); + return false; + } 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(value); +} + +void tst_QJniEnvironment::registerNativeMethodsByJclass() +{ + JNINativeMethod methods[] { + { "intCallbackFromJava", "(I)V", reinterpret_cast(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(clazz, "convertToInt", "(Ljava/lang/String;)V", + parameter.object()); + 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(clazz, staticMethodId, + parameter.object()); + 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"