diff --git a/src/androidextras/androidextras.pro b/src/androidextras/androidextras.pro index f3caeea8eb1b483e39d4cb16f7abad448d5f5e0b..24d43710ec1ef25e74d1ab5055c692d8f2fc9638 100644 --- a/src/androidextras/androidextras.pro +++ b/src/androidextras/androidextras.pro @@ -1,6 +1,7 @@ TARGET = QtAndroidExtras DEFINES += QT_NO_USING_NAMESPACE -QMAKE_DOCS = $$PWD/doc/qtandroidextras.qdocconf +QMAKE_DOCS = \ + $$PWD/doc/qtandroidextras.qdocconf QT -= gui QT += core-private load(qt_module) diff --git a/src/androidextras/doc/qtandroidextras.qdocconf b/src/androidextras/doc/qtandroidextras.qdocconf index c69586c11338990e43de628ea7bbb48aa78a1b73..16ed61195ae9038f3444600d6617a06a5e4be97d 100644 --- a/src/androidextras/doc/qtandroidextras.qdocconf +++ b/src/androidextras/doc/qtandroidextras.qdocconf @@ -2,7 +2,7 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtAndroidExtras description = Qt Android Extras Reference Documentation -url = http://qt-project.org/doc/qt-$QT_VER/qtandroidextras +url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION examplesinstallpath = qtandroidextras @@ -24,18 +24,16 @@ qhp.QtAndroidExtras.subprojects.classes.title = C++ Classes qhp.QtAndroidExtras.subprojects.classes.indexTitle = Qt Android Extras C++ Classes qhp.QtAndroidExtras.subprojects.classes.selectors = class fake:headerfile qhp.QtAndroidExtras.subprojects.classes.sortPages = true - -tagfile = ../../../doc/qtandroidextras/qtandroidextras.tags +qhp.QtAndroidExtras.subprojects.examples.title = Examples +qhp.QtAndroidExtras.subprojects.examples.indexTitle = Qt Android Extras Examples +qhp.QtAndroidExtras.subprojects.examples.selectors = fake:example depends += qtcore qtdoc - headerdirs += .. sourcedirs += .. exampledirs += ../../../examples/androidextras \ - ../ \ snippets -excludedirs += ../../../examples/widgets/doc imagedirs += images navigation.landingpage = "Qt Android Extras" navigation.cppclassespage = "Qt Android Extras C++ Classes" diff --git a/src/androidextras/doc/snippets/code/src_androidextras_qandroidjniobject.cpp b/src/androidextras/doc/snippets/code/src_androidextras_qandroidjniobject.cpp index 899ca74c882e5c47c5304445dd79837c4d60e952..6f545fba2ac6ada9656bbd9c3c300143f3cc22c7 100644 --- a/src/androidextras/doc/snippets/code/src_androidextras_qandroidjniobject.cpp +++ b/src/androidextras/doc/snippets/code/src_androidextras_qandroidjniobject.cpp @@ -81,3 +81,56 @@ void function() } } //! [Check for exceptions] + +//! [Registering native methods] +static void fromJavaOne(JNIEnv *env, jobject thiz, jint x) +{ + Q_UNUSED(env) + Q_UNUSED(thiz) + qDebug() << x << "< 100"; +} + +static void fromJavaTwo(JNIEnv *env, jobject thiz, jint x) +{ + Q_UNUSED(env) + Q_UNUSED(thiz) + qDebug() << x << ">= 100"; +} + +void registerNativeMethods() { + JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)}, + {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}}; + + QAndroidJniObject javaClass("my/java/project/FooJavaClass"); + QAndroidJniEnvironment env; + jclass objectClass = env->GetObjectClass(javaClass) + env->RegisterNatives(objectClass, + methods, + sizeof(methods) / sizeof(methods[0])); + env->DeleteLocalRef(objectClass); +} + +void foo() +{ + QAndroidJniObject::callStaticMethod("my/java/project/FooJavaClass", "foo", "(I)V", 10); // Output: 10 < 100 + QAndroidJniObject::callStaticMethod("my/java/project/FooJavaClass", "foo", "(I)V", 100); // Output: 100 >= 100 +} + +//! [Registering native methods] + +//! [Java native methods] +class FooJavaClass +{ + public static void foo(int x) + { + if (x < 100) + callNativeOne(x); + else + callNativeTwo(x); + } + +private static native void callNativeOne(int x); +private static native void callNativeTwo(int x); + +} +//! [Java native methods] diff --git a/src/androidextras/doc/src/qtandroidextras-examples.qdoc b/src/androidextras/doc/src/qtandroidextras-examples.qdoc new file mode 100644 index 0000000000000000000000000000000000000000..725641eeab7114c245d1d523160938c120f6cba7 --- /dev/null +++ b/src/androidextras/doc/src/qtandroidextras-examples.qdoc @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \group examples-qtandroidextras + \title Qt Android Extras Examples + \brief Examples in the Qt Android Extras module. + \ingroup all-examples + + These are the examples available in the \l{Qt Android Extras} module. + + \note These examples will only work on Android. +*/ diff --git a/src/androidextras/doc/src/qtandroidextras-index.qdoc b/src/androidextras/doc/src/qtandroidextras-index.qdoc index cf7d3df05e62fa4bfe427dd1bad963aa7685c115..74ca2493b09855b45c6d8e4d621141622dd16f39 100644 --- a/src/androidextras/doc/src/qtandroidextras-index.qdoc +++ b/src/androidextras/doc/src/qtandroidextras-index.qdoc @@ -40,8 +40,7 @@ #include <QtAndroidExtras> \endcode - To link against the module, add this line to your \l qmake \c - .pro file: + To link against the Qt Android Extras module, add this line to your project file: \code QT += androidextras @@ -53,4 +52,10 @@ \list \li \l{Qt Android Extras C++ Classes}{C++ Classes} \endlist + + \section1 Examples + + \list + \li \l{Qt Android Extras Examples} + \endlist */ diff --git a/src/androidextras/doc/src/qtandroidextras-module.qdoc b/src/androidextras/doc/src/qtandroidextras-module.qdoc index badb8a7d15d32a713e20748f9abc99f56f24e5ff..8a5eccb0be8e5f95abb8b4253467fb6aa74539e5 100644 --- a/src/androidextras/doc/src/qtandroidextras-module.qdoc +++ b/src/androidextras/doc/src/qtandroidextras-module.qdoc @@ -40,8 +40,7 @@ #include <QtAndroidExtras> \endcode - To link against the module, add this line to your \l qmake \c - .pro file: + To link against the Qt Android Extras module, add this line to your project file: \code QT += androidextras diff --git a/src/androidextras/jni/qandroidjniobject.cpp b/src/androidextras/jni/qandroidjniobject.cpp index 79740e35ae653309496ecbc96a9754f154d9ff8a..10576507a4a49d317d25b33ee541b83968284cf4 100644 --- a/src/androidextras/jni/qandroidjniobject.cpp +++ b/src/androidextras/jni/qandroidjniobject.cpp @@ -48,55 +48,193 @@ QT_BEGIN_NAMESPACE /*! \class QAndroidJniObject \inmodule QtAndroidExtras - \brief The QAndroidJniObject is a C++ wrapper around a Java class. + \brief Provides APIs to call Java code from C++. \since 5.2 - QAndroidJniObject provides APIs to call Java methods + \sa QAndroidJniEnvironment - \section1 JNI types + \section1 General Notes - Object types: \list - \li jobject - \li jclass - \li jstring - \li jarray - \li jobjectArray - \li jbooleanArray - \li jbyteArray - \li jcharArray - \li jshortArray - \li jintArray - \li jlongArray - \li jfloatArray - \li jdoubleArray + \li Class names needs to contain the fully-qualified class name, for example: \b"java/lang/String". + \li Method signatures are written as \b"(Arguments)ReturnType" + \li All object types are returned as a QAndroidJniObject. \endlist - Primitive types: - \list - \li jboolean - \li jbyte - \li jchar - \li jshort - \li jint - \li jlong - \li jfloat - \li jdouble - \endlist + \section1 Method Signatures + + For functions that take no arguments, QAndroidJniObject provides convenience functions that will use + the correct signature based on the provided template type. For example: + + \code + jint x = QAndroidJniObject::callMethod<jint>("getSize"); + QAndroidJniObject::callMethod<void>("touch"); + \endcode + + In other cases you will need to supply the signature yourself, and it is important that the + signature matches the function you want to call. The signature structure is \b \(A\)R, where \b A + is the type of the argument\(s\) and \b R is the return type. Array types in the signature must + have the \b\[ suffix and the fully-qualified type names must have the \b L prefix and \b ; suffix. + + The example below demonstrates how to call two different static functions. + \code + // Java class + package org.qtproject.qt5; + class TestClass + { + static String fromNumber(int x) { ... } + static String[] stringArray(String s1, String s2) { ... } + } + \endcode + + The signature for the first function is \b"\(I\)Ljava/lang/String;" + + \code + // C++ code + QAndroidJniObject stringNumber = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass", + "fromNumber" + "(I)Ljava/lang/String;", + 10); + \endcode - \section1 General Notes: + and the signature for the second function is \b"\(Ljava/lang/String;Ljava/lang/String;\)\[Ljava/lang/String;" + + \code + // C++ code + QAndroidJniObject string1 = QAndroidJniObject::fromString("String1"); + QAndroidJniObject string2 = QAndroidJniObject::fromString("String2"); + QAndroidJniObject stringArray = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass", + "stringArray" + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" + string1.object<jstring>(), + string2.object<jstring>()); + \endcode - - Class name strings needs to be the fully-qualified class name, for example: "java/lang/String". - - Method signatures are written as "(Arguments)ReturnType" - - All object types are returned as a QAndroidJniObject. - \section1 Handling Java exception: + \section1 Handling Java Exception - When calling Java functions that might throw an exception, it is important that you handle and - clear out the exception before continuing. + When calling Java functions that might throw an exception, it is important that you check, handle + and clear out the exception before continuing. + + \note It is unsafe to make a JNI call when there are exceptions pending. \snippet code/src_androidextras_qandroidjniobject.cpp Check for exceptions + \section1 Java Native Methods + + Java native methods makes it possible to call native code from Java, this is done by creating a + function declaration in Java and prefixing it with the \b native keyword. + Before a native function can be called from Java, you need to map the Java native function to a + native function in your code. Mapping functions can be done by calling the RegisterNatives() function + through the \l{QAndroidJniEnvironment}{JNI environment pointer}. + + The example below demonstrates how this could be done. + + Java implementation: + \snippet code/src_androidextras_qandroidjniobject.cpp Java native methods + + C++ Implementation: + \snippet code/src_androidextras_qandroidjniobject.cpp Registering native methods + + \section1 The Lifetime of a Java Object + + Most \l{Object types}{objects} received from Java will be local references and will only stay valid + in the scope you received them. After that, the object becomes eligible for garbage collection. If you + want to keep a Java object alive you need to either create a new global reference to the object and + release it when you are done, or construct a new QAndroidJniObject and let it manage the lifetime of the Java object. + \sa object() + + \note The QAndroidJniObject does only manage its own references, if you construct a QAndroidJniObject from a + global or local reference that reference will not be released by the QAndroidJniObject. + + \section1 JNI Types + + \section2 Object Types + \table + \header + \li Type + \li Signature + \row + \li jobject + \li {1, 3} L\e<fully-qulified-name>}; + \row + \li jclass + \row + \li jstring + \row + \li jobjectArray + \li [L\e<fully-qulified-name>; + \row + \li jarray + \li [\e<type> + \row + \li jbooleanArray + \li [Z + \row + \li jbyteArray + \li [B + \row + \li jcharArray + \li [C + \row + \li jshortArray + \li [S + \row + \li jintArray + \li [I + \row + \li jlongArray + \li [J + \row + \li jfloatArray + \li [F + \row + \li jdoubleArray + \li [D + \endtable + + + \section2 Primitive Types + \table + \header + \li Type + \li Signature + \row + \li jboolean + \li Z + \row + \li jbyte + \li B + \row + \li jchar + \li C + \row + \li jshort + \li S + \row + \li jint + \li I + \row + \li jlong + \li J + \row + \li jfloat + \li F + \row + \li jdouble + \li D + \endtable + + \section3 Other + \table + \header + \li Type + \li Signature + \row + \li void + \li V + \endtable + For more information about JNI see: \l http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html */ @@ -192,7 +330,7 @@ QT_BEGIN_NAMESPACE /*! \fn T QAndroidJniObject::callMethod(const char *methodName) const - Calls the method \a methodName and returs the value. + Calls the method \a methodName and returns the value. \code QAndroidJniObject myJavaString = ...; @@ -228,7 +366,7 @@ QT_BEGIN_NAMESPACE /*! \fn T QAndroidJniObject::callStaticMethod(jclass clazz, const char *methodName) - Calls the static method \a methodName on \a clazz and returns the value T. + Calls the static method \a methodName on \a clazz and returns the value. \code ... @@ -265,8 +403,7 @@ QT_BEGIN_NAMESPACE /*! \fn T QAndroidJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...) - Calls the static method \a methodName with \a signature on \a clazz and returns the value T, - unless T is void. + Calls the static method \a methodName with \a signature on \a clazz and returns the value. \code ... @@ -453,15 +590,15 @@ QT_BEGIN_NAMESPACE /*! \fn T QAndroidJniObject::object() const - Returns a jobject as type T. + Returns the object held by the QAndroidJniObject as type T. \code QAndroidJniObject string = QAndroidJniObject::fromString("Hello, JNI"); jstring jstring = string.object<jstring>(); \endcode - Note that the jobject is still owned by the QAndroidJniObject. If you want to keep the jobject valid - you should create a new QAndroidJniObject or make a new global reference to the jobject and + \note The returned object is still owned by the QAndroidJniObject. If you want to keep the object valid + you should create a new QAndroidJniObject or make a new global reference to the object and free it yourself. \snippet code/src_androidextras_qandroidjniobject.cpp QAndroidJniObject scope @@ -477,7 +614,7 @@ QT_BEGIN_NAMESPACE \fn QString QAndroidJniObject::toString() const Returns a QString with a string representation of the java object. - Calling this function on a Java String object is a convenient way of getting the actuall string + Calling this function on a Java String object is a convenient way of getting the actual string data. \code diff --git a/tests/auto/qandroidjnienvironment/qandroidjnienvironment.pro b/tests/auto/qandroidjnienvironment/qandroidjnienvironment.pro index 3c4fcf7b3e35d594ceb845f9bc339f719df6d885..b253c17ba54cb3daa256309b6e2969927e4d92b0 100644 --- a/tests/auto/qandroidjnienvironment/qandroidjnienvironment.pro +++ b/tests/auto/qandroidjnienvironment/qandroidjnienvironment.pro @@ -1,4 +1,4 @@ CONFIG += testcase TARGET = tst_qandroidjnienvironment -QT += testlib androidextras +QT += testlib androidextras core-private SOURCES += tst_qandroidjnienvironment.cpp diff --git a/tests/auto/qandroidjnienvironment/tst_qandroidjnienvironment.cpp b/tests/auto/qandroidjnienvironment/tst_qandroidjnienvironment.cpp index 307e6b751b3ba792e3f6f15e94ae93a7cbaaa1a3..3115ae32939d49625fc5a2e29b070ad1e37d7f9f 100644 --- a/tests/auto/qandroidjnienvironment/tst_qandroidjnienvironment.cpp +++ b/tests/auto/qandroidjnienvironment/tst_qandroidjnienvironment.cpp @@ -41,6 +41,7 @@ #include <QtTest/QtTest> #include <QtAndroidExtras/QAndroidJniEnvironment> +#include <QtCore/private/qjnihelpers_p.h> class tst_QAndroidJniEnvironment : public QObject { @@ -49,23 +50,19 @@ class tst_QAndroidJniEnvironment : public QObject private slots: void jniEnv(); void javaVM(); - -public: - static JavaVM *m_javaVM; }; -JavaVM *tst_QAndroidJniEnvironment::m_javaVM = 0; - void tst_QAndroidJniEnvironment::jniEnv() { - QVERIFY(m_javaVM); + JavaVM *javaVM = QtAndroidPrivate::javaVM(); + QVERIFY(javaVM); { QAndroidJniEnvironment env; // JNI environment should now be attached to the current thread JNIEnv *jni = 0; - QCOMPARE(m_javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK); + QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK); JNIEnv *e = env; QVERIFY(e); @@ -84,28 +81,22 @@ void tst_QAndroidJniEnvironment::jniEnv() // The environment should automatically be detached when QAndroidJniEnvironment goes out of scope JNIEnv *jni = 0; - QCOMPARE(m_javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_EDETACHED); + QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_EDETACHED); } void tst_QAndroidJniEnvironment::javaVM() { - QVERIFY(m_javaVM); + JavaVM *javaVM = QtAndroidPrivate::javaVM(); + QVERIFY(javaVM); QAndroidJniEnvironment env; - QCOMPARE(env.javaVM(), m_javaVM); + QCOMPARE(env.javaVM(), javaVM); JavaVM *vm = 0; QCOMPARE(env->GetJavaVM(&vm), JNI_OK); QCOMPARE(env.javaVM(), vm); } -Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) -{ - Q_UNUSED(reserved) - tst_QAndroidJniEnvironment::m_javaVM = vm; - return JNI_VERSION_1_6; -} - QTEST_APPLESS_MAIN(tst_QAndroidJniEnvironment) #include "tst_qandroidjnienvironment.moc" diff --git a/tests/auto/qandroidjniobject/tst_qandroidjniobject.cpp b/tests/auto/qandroidjniobject/tst_qandroidjniobject.cpp index 4b8f1d58157767acd1b425655ad0e16ddfab8ec2..47b476a434554e48cb354f2fd85f8b7dbbf617a7 100644 --- a/tests/auto/qandroidjniobject/tst_qandroidjniobject.cpp +++ b/tests/auto/qandroidjniobject/tst_qandroidjniobject.cpp @@ -97,13 +97,8 @@ private slots: void getIntField(); void cleanupTestCase(); - -public: - static jclass m_activityDelegateClass; }; -jclass tst_QAndroidJniObject::m_activityDelegateClass = 0; - tst_QAndroidJniObject::tst_QAndroidJniObject() { } @@ -686,7 +681,7 @@ void tst_QAndroidJniObject::getBooleanField() { QVERIFY(m_activityDelegateClass); - QAndroidJniObject obj(m_activityDelegateClass); + QAndroidJniObject obj("org/qtproject/qt5/android/QtActivityDelegate"); QVERIFY(obj.isValid()); QVERIFY(!obj.getField<jboolean>("m_fullScreen")); @@ -694,9 +689,7 @@ void tst_QAndroidJniObject::getBooleanField() void tst_QAndroidJniObject::getIntField() { - QVERIFY(m_activityDelegateClass); - - QAndroidJniObject obj(m_activityDelegateClass); + QAndroidJniObject obj("org/qtproject/qt5/android/QtActivityDelegate"); QVERIFY(obj.isValid()); jint res = obj.getField<jint>("m_currentRotation"); @@ -704,27 +697,6 @@ void tst_QAndroidJniObject::getIntField() QCOMPARE(res, -1); } -Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) -{ - typedef union { - JNIEnv *nenv; - void *venv; - } _JNIEnv; - - _JNIEnv uenv; - uenv.venv = Q_NULLPTR; - - if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) - return JNI_ERR; - - JNIEnv *env = uenv.nenv; - - jclass clazz = env->FindClass("org/qtproject/qt5/android/QtActivityDelegate"); - tst_QAndroidJniObject::m_activityDelegateClass = (jclass)env->NewGlobalRef(clazz); - - return JNI_VERSION_1_6; -} - QTEST_APPLESS_MAIN(tst_QAndroidJniObject) #include "tst_qandroidjniobject.moc"