Search code examples
androidqtjava-native-interface

Where does Qt look for java classes when using JNI


I'm trying to use JNI in Qt. I would like to know where should I put the java classes. There are many posts about JNI on the internet, but they assume you know where to put the classes and only show how to call the methods.

I found that the variable: ANDROID_PACKAGE_SOURCE_DIR must be set. I set this variable, but it didn't work.

I have a folder android in the project root folder. My files tree is something like this:

-root-project
    -android
        -app1
            -MyJavaClass.class
            -MyJavaClass.java

The class has this package: package app1;

In the .pro file, I've tried this:

android{
    QT += androidextras
    ANDROID_PACKAGE_SOURCE_DIR=$$_PRO_FILE_PWD_/android
}

and this:

android{
    QT += androidextras
    ANDROID_PACKAGE_SOURCE_DIR=$$PWD/android
}

I'm trying to use the class in this way:

int AccessLocalData::fibonacci(int n) {
    return QAndroidJniObject::callStaticMethod<jint>("app1/MyJavaClass", "fibonacci", "(I)I", n);
}

The better posts I found were these:

Using Qt/C++ to call Java code through JNI. FindClass does not find class

How to use QAndroidJniObject to call Java code from inside Qt?

Both of them said to use ANDROID_PACKAGE_SOURCE_DIR, but once this didn't work for me, I think I need a deeper explanation of how this whole thing works.

The error thrown is this:

W System.err: java.lang.ClassNotFoundException: Didn't find class "app1.MyJavaClass" on path: DexPathList[[],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
W System.err:   at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W System.err:   at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
W System.err:   at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
W System.err:   Suppressed: java.lang.ClassNotFoundException: Didn't find class "app1.MyJavaClass" on path: DexPathList[[zip file "/data/app/org.qtproject.example.INF628-1/base.apk"],nativeLibraryDirectories=[/data/app/org.qtproject.example.INF628-1/lib/arm, /data/app/org.qtproject.example.INF628-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]
W System.err:       at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W System.err:       at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
W System.err:       at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
W System.err:       ... 1 more
W System.err:       Suppressed: java.lang.ClassNotFoundException: app1.MyJavaClass
W System.err:           at java.lang.Class.classForName(Native Method)
W System.err:           at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
W System.err:           at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
W System.err:           at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
W System.err:           ... 2 more
W System.err:       Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

Solution

  • They should reside in src folder under your ANDROID_PACKAGE_SOURCE_DIR. Lets say I have a java package org.me.mypack , then what I do is create the FQ path for the package under: ANDROID_PACKAGE_SOURCE_DIR/src

    Suppose my ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android

    eventually I will have this path:

    $$PWD/android/src/org.me.mypack

    and call a static method in my class:

    QAndroidJniObject::callStaticMethod<void>("org.me.mypack/Utilities",
                                            "requestPermissions",
                                            "(Landroid/app/Activity;)V",
                                            QtAndroid::androidActivity().object());
    

    So in your case, you ought to create your package path:

    ANDROID_PACKAGE_SOURCE_DIR/src/app1 --> $$PWD/android/src/app1

    and put your java class file MyJavaClass.java and other java classes for the package in that path.

    If you haven't already done, check JNI Crash Course, which uses same call as your example, but with different package!

    Extending your Qt Android application using JNI

    in the example note the file path: //java file android/src/com/kdab/training/MyJavaClass.java and the call:

    return QAndroidJniObject::callStaticMethod<jint>
     ("com/kdab/training/MyJavaClass" // java class name
     , "fibonacci" // method name
     , "(I)I" // signature
     , n);