Solved 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