Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Callback from Android to QML



  • Hey everyone :D
    I am relatively new to Qt / QML, so please excuse me, if I am not getting everything right.

    Currently I am integrating several Plugins into an existing Qml Application, with specific implementations for Android and iOS.

    Basically, my project looks something like the following.

    To achieve this, I have a myfunctions.h header file, where I specified all my methods:

    #ifndef MYFUNCTIONS_H
    #define MYFUNCTIONS_H
    
    #include <QQuickItem> // QObject, QString, ...
    
    class MyFunctions : public QObject {
    
        Q_OBJECT
        Q_DISABLE_COPY(MyFunctions)
    
        Q_PROPERTY(QString myString READ myString WRITE setMyString NOTIFY myStringChanged)
    
    public:
        explicit MyFunctions(QObject *parent = 0);
    
        void setMyString(const QString &string);
        QString myString() const;
    
    
    signals:
        void myStringChanged();
    
    private slots:
        void updateMyString();
    
    private:
        QString m_myString;
    
    };
    #endif // MYFUNCTIONS_H
    
    

    Now I am able to implement the iOs calls in an myfunctions_ios.mm file:

    #include "myfunctions.h"
    // include native libraries and functions
    
    // For Native iOS Functions
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface QIOSApplicationDelegate
    @end
    
    @interface QIOSApplicationDelegate(AppDelegate)
    @end
    
    @implementation QIOSApplicationDelegate (AppDelegate)
    @end
    
    MyFunctions:: MyFunctions(QObject *parent) : QObject(parent){
        connect(this, SIGNAL(myStringChanged()), this, SLOT(updateMyString()));
    }
    
    void MyFunctions::setMyString(const QString &string) {
        m_myString = string;
        emit myStringChanged();
    }
    
    QString MyFunctions::myString() const {
        return m_myString;
    }
    
    void MyFunctions::updateMyString() {
        NSLog(@"iOS updateMyString with current value: %@", m_notification.toNSString());
    
        // call external Library
        // [LIB function: params]
    }
    
    

    and for android in myfunctions_android.cpp:

    #include "myfunctions.h"
    
    #include <QtAndroidExtras>
    
    
    MyFunctions:: MyFunctions(QObject *parent) : QObject(parent){
        connect(this, SIGNAL(myStringChanged()), this, SLOT(updateMyString()));
    }
    
    void MyFunctions::setMyString(const QString &string) {
        m_myString = string;
        emit myStringChanged();
    }
    
    QString MyFunctions::myString() const {
        return m_myString;
    }
    
    void MyFunctions::updateMyString() {
        // call JNI function
        QAndroidJniObject javaNotification = QAndroidJniObject::fromString(m_myString);
        QAndroidJniObject::callStaticMethod<void>("path/to/my/class",
                                           "method name",
                                           "(Ljava/lang/String;)V",
                                           javaNotification.object<jstring>());
    }
    
    

    So I need an additional .java file to implement the function called per JNI from above.
    I have managed to get along with this so far,
    but now I need a callback or something like this, to return changes from inside the iOs/Android Application back to my QML app.

    For iOs this wasn't much of a problem, because I was able to just emit signals directly from the myfunctions_ios.mm file.

    But how do I achieve this in Android?

    Is there a way back from my Android .java code, back to Qml?
    How can I notify my QML app if something on the android device is changed (for example permissions, push-notifications, ...) ?

    Can anyone suggest me, how to solve this or point me into any direction?

    best Regards,
    SyntaX



  • This should help: https://doc.qt.io/qt-5/qandroidjniobject.html#java-native-methods
    Basically you have to

    • define your C++ functions that will be called from Java
    • register these functions
    • declare them in java using the "native" keyword
    • call them in java as normal functions

    Hope this helps,

    Marc



  • This should help: https://doc.qt.io/qt-5/qandroidjniobject.html#java-native-methods
    Basically you have to

    • define your C++ functions that will be called from Java
    • register these functions
    • declare them in java using the "native" keyword
    • call them in java as normal functions

    Hope this helps,

    Marc



  • thanks for your reply, this was indeed very helpful :D

    I declared my "callback" function in the .java file with the native keyword:

    public class MyJavaClass {
    
        // callback handler to send notifications back to c++
        public static native void StringChanged(String mystring);
    
        public void doSomeStuff () {
            // do stuff, then emit changes back to c++
            StringChanged("my new string");
        }
    }
    

    Then I registered those functions in my implementation

    void StringChangeReceived(JNIEnv *env, jobject obj, jstring newstring) {
    
        Q_UNUSED(obj)
    
        QString qString(env->GetStringUTFChars(newstring, 0));
        qDebug().noquote().nospace() << "callback from Android received: " << qString;
    
        // to be able to emit the changes I either need an instance or (what I decided) a classic singleton
        if (nullptr != instance) {
            instance->myStringChanged(qString);
        }
    }
    
    void init () {
        // install callback for notifications received
        QAndroidJniEnvironment env;
        JNINativeMethod methods[] = {
            {
                "StringChanged",
                "(Ljava/lang/String;)V",
                reinterpret_cast<void*>(StringChangeReceived)
            }
        };
        QAndroidJniObject javaClass("path/to/my/JavaClass");
        jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
    
        env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
        env->DeleteLocalRef(objectClass);
    }
    

    With this I realised at least a most basic full roundtrip, from my qml -> over the c++ class -> to the native java class on the android device -> and back over c++ -> to qml. :DDD

    Is it common practice to use that in combination with singletons? (Because I think I need them everywhere because of the static call?)
    Are there best practices or key values to that kind of implementation?

    best regards
    an thank you very much,

    SyntaX


Log in to reply