QAndroidJniObject: calling non static methods of a custom java class
-
wrote on 22 Nov 2015, 19:10 last edited by FlyingSheep
Does anybody have a working example of how to use QAndroidJniObject to call the NON static methods of a custom java class?
So far I can successfully:
- call static methods of my custom java class
- instantiate and call methods of standard java classes (e.g. /java/util/Date)
However when I attempt to instantiate my custom class in order to call the non static methods, isValid() returns false. Calling the methods give JNI errors.
My custom java class looks like this:
package org.flyingsheep.landed.calljavaobject; import org.qtproject.qt5.android.bindings.QtApplication; import org.qtproject.qt5.android.bindings.QtActivity; import android.util.Log; public class MyJavaClass extends QtActivity { public MyJavaClass() { Log.d(QtApplication.QtTAG, "MyJavaClass Constructor"); } public static int testStatic(int n) { Log.d(QtApplication.QtTAG, "java: test static"); return n+1; } public int testNonStatic(int n) { Log.d(QtApplication.QtTAG, "java: test non static"); return n+1; }
}
My c++ calling class looks like this:
#include "javacaller.h" #include <QtAndroidExtras/QAndroidJniObject> #include <QtAndroidExtras/QAndroidJniEnvironment> #include <QDebug> JavaCaller::JavaCaller(QObject *parent) : QObject(parent) { } void JavaCaller::callJava() { //1) Test if calls can be made to standard objects (e.g. java/util/Date) QAndroidJniObject stdObj("java/util/Date"); qDebug() << "Date is valid? " << stdObj.isValid(); jlong date = stdObj.callMethod<jlong>("getTime"); qDebug() << "getTime returned " << date; //2) Test static call to custom class qDebug() << "javaCaller: call static method of custom class"; int staticTest = QAndroidJniObject::callStaticMethod<int> ("org/flyingsheep/landed/calljavaobject/MyJavaClass", "testStatic", "(I)I", 99); qDebug() << "javaCaller: testStatic returned: " << staticTest; //3) Test non static call to custom class qDebug() << "JavaCaller: testing non static calls to custom class"; QAndroidJniObject javaCaller("org/flyingsheep/landed/calljavaobject/MyJavaClass"); qDebug() << "MyJavaClass is valid? " << javaCaller.isValid(); //if class was valid, I would call methods here //Error handling, comment out this section to see the JNI error QAndroidJniEnvironment env; if (env->ExceptionCheck()) { qDebug() << "JNI Exception trapped"; env->ExceptionClear(); } } JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { qDebug() << "ERROR: JNI version"; return JNI_ERR; } jclass javaClass = env->FindClass("org/flyingsheep/landed/calljavaobject/MyJavaClass"); if (!javaClass) { qDebug() << "ERROR: class not found"; return JNI_ERR; } else { qDebug() << "JNI Success: class MyJavaClass found"; } return JNI_VERSION_1_6; }
in my AndroidManifest.xml I have
<activity ...... android:name="org.flyingsheep.landed.calljavaobject.MyJavaClass" android:label="CallJavaObject" .... </activity>
Any ideas why isValid() returns false? JNI_OnLoad finds the custom java class, and I can call non static methods.
b.t.w I have spent hours searching the internet. The only examples I can find are of calls to static methods.
The closest hit was https://github.com/benlau/qtandroidexamplecode/blob/master/qtandroidrunner/main.cpp
While in that example non static methods are called, they are standard android classes, not custom!I am running Qt5.5.1, and testing against a Galaxy Note 3
-
wrote on 22 Nov 2015, 23:06 last edited by
You should not instantiate your activity, because it's already instantiated by Qt. You can get its instance this way:
QtAndroid::androidActivity().callMethod<jint>("testNonStatic", "(I)I");
-
wrote on 23 Nov 2015, 06:46 last edited by FlyingSheep
Thanks Leonardo, that works perfectly, and solves the immediate problem.
QtAndroid::androidActivity().callMethod to be a little known technique. If I goggle for it, I get very few hits.
Now some follow up questions.
-
According to the docu: http://doc.qt.io/qt-5/qtandroid.html androidActivity() gives a handle to the main activity. But what if I have multiple activities? How do I call non static methods from the “other” activities.
-
Does a java class called from the QT C++ side have to be an Activity? My nonsense example does not use any activity functionality. If not, how are non static methods called from non activity type java classes?
The architectural issue behind all of this is a SailfishOS app I am porting to Android. This will likely require multiple entry points from C++ to java to access Android functionality not (yet) directly supported by Qt on Android. (e.g. send SMS, access Contacts, activate LED). I had envisaged that each of these functionalities would be accessed via a separate java class / activity.
-
-
wrote on 23 Nov 2015, 13:31 last edited by
Hi. Qt is not designed to handle multiple activities. If you need multiple interfaces, you could split your logic using components like Loader and StackView (assuming you're using QML). Any activity other than the main one, would have to be coded natively using Java, and that would defeat the whole cross-platform thing. The functionalities you mentioned seem quite simple. I would say you could have them as methods in your extension from QtActivity.
There's some additional info here:
https://blog.qt.io/blog/2013/07/23/anatomy-of-a-qt-5-for-android-application/
-
wrote on 23 Nov 2015, 15:27 last edited by
Thanks once again.
Eskil Abrahamsen Blomfeldt ’s blog is good. I used one of his articles last week as a base for my first Qt Android Proof-of-Concept app - to send SMSes.
The whole Qt multi-platform thing (on Android at least) is to some extent hamstrung by the fact that things that can be done on other mobile platforms via QML / C++ can (at the moment) only be done via java calls to Android’s java APIs. (e.g. send SMS, list contacts). On the other hand the GPS functionality was pretty much cut and paste from SailfishOS to Android, as Qt Positioning / Location is supported on both platforms.
The existing SailfishOS app is mainly QML, a group of pagestack pages. For things that could not be done directly / easily with QML, I import C++ helper classes (e.g. SMSHelper, TorchHelper) which call the Qt / SailfishOS C++ APIs.
I had assumed that in the ported Qt Android version of the app, each of the equivalent C++ helper classes would call its own dedicated java activity class.
However if a Qt Android App can only have one activity class, then it looks like the C++ helper classes will have to share one central fatish java activity class. This java class would be the central “gateway” for all Android API calls from the various parts of my app.
1/5