I am new to JNI and this is my first program trying to call Java code from C++. I am using Qt 5.2 and I am writing an Android application.
I am not able to find my java class and load it into my C++ program. I have read a lot of posts here on stack overflow and other places and it seems to be a common problem, but I have not been able to solve mine yet..
I am also not sure if the Java VM is properly set up as the Qt documentation on the QAndroidJniEnvironment is minimal.
I am looking for a solution on how to be able to find my java class. I am also appreciating general feedback on other sections of the code (I assume there might be more errors).
Error msg:
Starting remote process.D/dalvikvm(24911): GC_CONCURRENT freed 384K, 5% free 9180K/9596K, paused 1ms+2ms, total 15ms
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libgnustl_shared.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libgnustl_shared.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libgnustl_shared.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Core.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Core.so 0x428b2360
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Network.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Network.so 0x428b2360
I/Qt (24911): Network start
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Qml.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Qml.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libQt5Qml.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Gui.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Gui.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libQt5Gui.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Quick.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5Quick.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libQt5Quick.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5QuickParticles.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5QuickParticles.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libQt5QuickParticles.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5AndroidExtras.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5AndroidExtras.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/data/org.qtproject.example.AndroidTest/lib/libQt5AndroidExtras.so 0x428b2360, skipping init
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/plugins/platforms/android/libqtforandroidGL.so 0x428b2360
D/dalvikvm(24911): Added shared lib /data/data/org.qtproject.example.AndroidTest/plugins/platforms/android/libqtforandroidGL.so 0x428b2360
I/Qt (24911): qt start
W/dalvikvm(24911): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtMessageDialogHelper'
D/dalvikvm(24911): Trying to load lib /data/data/org.qtproject.example.AndroidTest/lib/libQt5QuickParticles.so 0x428b2360
D/dalvikvm(24911): Shared lib '/data/data/org.qtproject.example.AndroidTest/lib/libQt5QuickParticles.so' already loaded in same CL 0x428b2360
D/dalvikvm(24911): Trying to load lib /data/app-lib/org.qtproject.example.AndroidTest-1/libAndroidTest.so 0x428b2360
D/Qt (24911): qml\qqmlengine.cpp:1451 (QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool)): QML debugging is enabled. Only use this in a safe environment.
D/dalvikvm(24911): Added shared lib /data/app-lib/org.qtproject.example.AndroidTest-1/libAndroidTest.so 0x428b2360
D/dalvikvm(24911): No JNI_OnLoad found in /data/app-lib/org.qtproject.example.AndroidTest-1/libAndroidTest.so 0x428b2360, skipping init
W/Qt (24911): kernel\qcoreapplication.cpp:416 (QCoreApplicationPrivate::QCoreApplicationPrivate(int&, char**, uint)): WARNING: QApplication was not created in the main() thread.
W/dalvikvm(24911): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtNativeInputConnection'
W/dalvikvm(24911): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtExtractedText'
I/Adreno-EGL(24911): <qeglDrvAPI_eglInitialize:320>: EGL 1.4 QUALCOMM Build: I0404c4692afb8623f95c43aeb6d5e13ed4b30ddbDate: 11/06/13
D/Qt (24911): fontdatabases\basic\qbasicfontdatabase.cpp:246 (static QStringList QBasicFontDatabase::addTTFile(const QByteArray&, const QByteArray&)): FT_New_Face failed with index 0 : 90
D/Qt (24911): ..\AndroidTest\jnimathcppwrapper.cpp:18 (jniMathCppWrapper::jniMathCppWrapper()): JniMath class not found
D/Qt (24911): ..\AndroidTest\jnimathcppwrapper.cpp:43 (int jniMathCppWrapper::eleven()): Enter eleven
F/libc (24911): Fatal signal 11 (SIGSEGV) at 0x0000002c (code=1), thread 24933 (ple.AndroidTest)
Java Class:
package org.app.test;
public class JniMath {
public JniMath()
{
}
public int eleven()
{
return 11;
}
}
.pro file:
# Add more folders to ship with the application, here
folder_01.source = qml/AndroidTest
folder_01.target = qml
DEPLOYMENTFOLDERS = folder_01
# Additional import path used to resolve QML modules in Creator's code model
QML_IMPORT_PATH =
# The .cpp file which was generated for your project. Feel free to hack it.
SOURCES += main.cpp #\
# jnimathcppwrapper.cpp
# Installation path
# target.path =
# Please do not modify the following two lines. Required for deployment.
include(qtquick2applicationviewer/qtquick2applicationviewer.pri)
qtcAddDeployment()
RESOURCES += \
resources.qrc
QT += androidextras
OTHER_FILES += \
android/src/org/app/test/JniMath.java
HEADERS += #\
# jnimathcppwrapper.h
android {
SOURCES += jnimathcppwrapper.cpp
HEADERS += jnimathcppwrapper.h
}
main.cpp:
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "jnimathcppwrapper.h"
#include <QDebug>
#include <QString>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/AndroidTest/main.qml"));
viewer.showExpanded();
jniMathCppWrapper *test = new jniMathCppWrapper();
qDebug() << QString::number(test->eleven());
return app.exec();
}
jnimathcppwrapper.h:
#ifndef JNIMATHCPPWRAPPER_H
#define JNIMATHCPPWRAPPER_H
#include <QtAndroidExtras>
class jniMathCppWrapper
{
public:
jniMathCppWrapper();
int eleven();
private:
jobject jniMathObject;
};
#endif // JNIMATHCPPWRAPPER_H
jnimathcppwrapper.cpp:
#include "jnimathcppwrapper.h"
#include <QtAndroidExtras>
#include <QDebug>
#include <jni.h>
static jclass jniMathClassID = 0;
static jmethodID jniMathConstructorMethodID = 0;
static jmethodID jniMathElevenMethodID = 0;
jniMathCppWrapper::jniMathCppWrapper()
{
QAndroidJniEnvironment qjniEnv;
//Get JniMath class ID.
jniMathClassID = qjniEnv->FindClass("android/src/org/app/test/JniMath");
if(jniMathClassID == NULL)
{
qDebug() << "JniMath class not found";
return;
}
//Get constructor method ID
jniMathConstructorMethodID = qjniEnv->GetMethodID(jniMathClassID, "<init>", "void(V)");
if(jniMathConstructorMethodID == NULL)
{
qDebug() << "JniMath constructor not found";
return;
}
//Create new Java object and calling the selected constructor.
jniMathObject = qjniEnv->NewObject(jniMathClassID, jniMathConstructorMethodID);
if(jniMathObject == NULL)
{
qDebug() << "JniMath Java object could not be constructed";
return;
}
}
int jniMathCppWrapper::eleven()
{
QAndroidJniEnvironment qjniEnv;
qDebug() << "Enter eleven";
//Get eleven method ID
jniMathElevenMethodID = qjniEnv->GetMethodID(jniMathClassID, "eleven", "void(V)");
if(jniMathElevenMethodID == NULL)
{
qDebug() << "JniMath class, eleven method not found";
return 9;
}
jint res = qjniEnv->CallIntMethod(jniMathObject, jniMathElevenMethodID);
return (int) res;
}
Project structure:
EDIT:
I also tried a different approach leading to the same error:
QAndroidJniObject *myJavaClass = new QAndroidJniObject("android/src/org/app/test/JniMath");
if(myJavaClass->isValid())
{
qDebug() << "Class found!";
}
else
{
qDebug() << "Class NOT found!";
}
When trying to load java/lang/String instead, both methods above find the class.
EDIT:
Error log:
D/Qt ( 3385): qml\qqmlengine.cpp:1451 (QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool)): QML debugging is enabled. Only use this in a safe environment.
D/dalvikvm( 3385): Added shared lib /data/app-lib/org.qtproject.example.AndroidTest-1/libAndroidTest.so 0x428b67f8
D/Qt ( 3385): ..\AndroidTest\jnimathcppwrapper.cpp:68 (jint JNI_OnLoad(JavaVM*, void*)): Class NOT found
D/AndroidRuntime( 3385): Shutting down VM
W/dalvikvm( 3385): threadid=1: thread exiting with uncaught exception (group=0x41fecba8)
E/AndroidRuntime( 3385): FATAL EXCEPTION: main
E/AndroidRuntime( 3385): Process: org.qtproject.example.AndroidTest, PID: 3385
E/AndroidRuntime( 3385): java.lang.NoClassDefFoundError: org/app/test/JniMath
E/AndroidRuntime( 3385): at java.lang.Runtime.nativeLoad(Native Method)
E/AndroidRuntime( 3385): at java.lang.Runtime.doLoad(Runtime.java:421)
E/AndroidRuntime( 3385): at java.lang.Runtime.loadLibrary(Runtime.java:362)
E/AndroidRuntime( 3385): at java.lang.System.loadLibrary(System.java:526)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.loadApplication(QtActivity.java:235)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.startApp(QtActivity.java:522)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.onCreate(QtActivity.java:744)
E/AndroidRuntime( 3385): at android.app.Activity.performCreate(Activity.java:5231)
W/dalvikvm( 3385): threadid=1: thread exiting with uncaught exception (group=0x41fecba8)
E/AndroidRuntime( 3385): FATAL EXCEPTION: main
E/AndroidRuntime( 3385): Process: org.qtproject.example.AndroidTest, PID: 3385
E/AndroidRuntime( 3385): java.lang.NoClassDefFoundError: org/app/test/JniMath
E/AndroidRuntime( 3385): at java.lang.Runtime.nativeLoad(Native Method)
E/AndroidRuntime( 3385): at java.lang.Runtime.doLoad(Runtime.java:421)
E/AndroidRuntime( 3385): at java.lang.Runtime.loadLibrary(Runtime.java:362)
E/AndroidRuntime( 3385): at java.lang.System.loadLibrary(System.java:526)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.loadApplication(QtActivity.java:235)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.startApp(QtActivity.java:522)
E/AndroidRuntime( 3385): at org.qtproject.qt5.android.bindings.QtActivity.onCreate(QtActivity.java:744)
E/AndroidRuntime( 3385): at android.app.Activity.performCreate(Activity.java:5231)
E/AndroidRuntime( 3385): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
E/AndroidRuntime( 3385): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
E/AndroidRuntime( 3385): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
E/AndroidRuntime( 3385): at android.app.ActivityThread.access$800(ActivityThread.java:135)
E/AndroidRuntime( 3385): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
E/AndroidRuntime( 3385): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 3385): at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime( 3385): at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime( 3385): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 3385): at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime( 3385): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
E/AndroidRuntime( 3385): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
E/AndroidRuntime( 3385): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 3385): Caused by: java.lang.ClassNotFoundException: Didn't find class "org.app.test.JniMath" on path: DexPathList[[zip file "/data/app/org.qtproject.example.AndroidTest-1.apk"],nativeLibraryDirectories=[/data/app-lib/org.qtproject.example.AndroidTest-1, /vendor/lib, /system/lib]]
E/AndroidRuntime( 3385): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E/AndroidRuntime( 3385): at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
E/AndroidRuntime( 3385): at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
E/AndroidRuntime( 3385): ... 21 more
There is no doubt that the root problem here is the same as in FindClass from any thread in Android JNI, but the question asked here is a very different question, IMO. The author was not looking for a generic way to access the ClassLoader from native threads. He (or she) was looking for an easy and efficient way to access a Java callback from Qt-based native code. Therefore all discussion of how class loader works in Android and how this can be patched is irrelevant for him. If the author thinks otherwise, I will gladly agree to close this question as duplicate
Please consider the previous comment void. This question has nothing to do with multithreading. It's all about how to set up an Android Qt application such that it could use custom Java classes, similar to the sample.
I used this example http://www.gnuton.org/blog/2014/01/invoking-qtc-code-from-the-java-side-of-qt-for-android-application/
This is how I did it. In my java main class I added a static method to call the same activity (because it has to be static):
static public void startFacebookActivity() {
String msgTag = "FACEBOOK_APP";
try {
Log.v(msgTag, "starting activity");
Activity mother = QtNative.activity();
Log.v(msgTag, mother.toString());
Log.v(msgTag, MainActivity.class.getName());
Intent intent = new Intent(mother, MainActivity.class);
mother.startActivity(intent);
} catch (Exception e) {
Log.e(msgTag, e.toString());
e.printStackTrace();
}
}
Then in my header file I added this:
class FacebookAndroid : public QObject {
Q_OBJECT
public:
FacebookAndroid(QObject *parent = 0);
public slots:
void startAndroidFacebook();
};
and In my cpp file I just called my java method like this:
void FacebookAndroid::startAndroidFacebook() {
QAndroidJniObject::callStaticMethod<void>("org.qtproject.example.MainActivity",
"startFacebookActivity",
"()V");
Is your java source directory copied into the android-build directory? The android folder in your source folder needs to be listed in the qmake variable ANDROID_PACKAGE_SOURCE_DIR see: http://qt-project.org/doc/qt-5/deployment-android.html#qmake-variables
The easiest way to get FindClass to work will be to call it from within the JNI_OnLoad function. This function will automatically be called by JNI at load time in the correct thread. The old version of the notificationclient example used this method prior to commit #6500083. This is a summary of the relevant portion of the androidjnibindings.cpp file:
#include <QtAndroidExtras/QJNIObject>
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK) {
qFatal("Couldn't initialize environment!");
return -1;
}
jclass clazz = env->FindClass("org/qtproject/example/notification/NotificationClient");
if (clazz == 0) {
//
}
return JNI_VERSION_1_4;
}
This isn't in the 5.2 example code as this is no longer needed in order to call Java from the C++ code, but I'm not aware of a QT5.2 example showing calling C from Java.
jniMathClassID = qjniEnv->FindClass("android/src/org/app/test/JniMath");
Instead of "android/src/org/app/test/JniMath" try to give the packagename: "org/app/test/JniMath" and put it in
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
qCritical()<<"Can't get the enviroument";
return -1;
}
s_javaVM = vm; // cache the JavaVM pointer
jclass clazz= env->FindClass("org/app/test/JniMath");
jniMathClassID = (jclass)env->NewGlobalRef(tmp);
}
Something like this...
User contributions licensed under CC BY-SA 3.0