使用Qt/C++通过JNI调用Java代码。FindClass 找不到类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20851342/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:47:44  来源:igfitidea点击:

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

javaandroidc++qtjava-native-interface

提问by iQt

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.

我是 JNI 的新手,这是我第一个尝试从 C++ 调用 Java 代码的程序。我正在使用 Qt 5.2 并且我正在编写一个 Android 应用程序。

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..

我无法找到我的 java 类并将其加载到我的 C++ 程序中。我在这里阅读了很多关于堆栈溢出和其他地方的帖子,这似乎是一个常见问题,但我还没有解决我的问题。

I am also not sure if the Java VM is properly set up as the Qt documentation on the QAndroidJniEnvironment is minimal.

我也不确定 Java VM 是否正确设置,因为 QAndroidJniEnvironment 上的 Qt 文档很少。

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).

我正在寻找有关如何找到我的 java 类的解决方案。我也很欣赏对代码其他部分的一般反馈(我认为可能会有更多错误)。

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:

Java类:

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:

主.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:

jnimathcppwrapper.h:

#ifndef JNIMATHCPPWRAPPER_H
#define JNIMATHCPPWRAPPER_H

#include <QtAndroidExtras>

class jniMathCppWrapper
{
public:
    jniMathCppWrapper();
    int eleven();

private:
    jobject jniMathObject;
};

#endif // JNIMATHCPPWRAPPER_H

jnimathcppwrapper.cpp:

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:

项目结构:

enter image description here

在此处输入图片说明

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.

当尝试加载 java/lang/String 时,上述两种方法都会找到该类。

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.access0(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

Comment: why this question is not a duplicate (by Alex Cohn)

评论:为什么这个问题不是重复的(Alex Cohn)

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 authorwas 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

毫无疑问,这里的根本问题与Android JNI 中任何线程的 FindClass 中的问题相同,但这里提出的问题是一个非常不同的问题,IMO。在笔者不是在寻找从本地线程访问的类加载器的通用方式。他(或她)正在寻找一种简单有效的方法来从基于 Qt 的本机代码访问 Java 回调。因此,所有关于类加载器如何在 Android 中工作以及如何修补它的讨论对他来说都无关紧要。如果作者不这么认为,我很乐意同意以重复的形式结束这个问题

Update: this question is not even related to JNI threads

更新:这个问题甚至与 JNI 线程无关

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.

请考虑之前的评论无效。这个问题与多线程无关。这完全是关于如何设置一个 Android Qt 应用程序,以便它可以使用自定义 Java 类,类似于示例.

回答by cstream

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

你的java源目录是否复制到android-build目录中?源文件夹中的 android 文件夹需要列在 qmake 变量 ANDROID_PACKAGE_SOURCE_DIR 中,请参阅:http: //qt-project.org/doc/qt-5/deployment-android.html#qmake-variables

回答by sj0h

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:

使 FindClass 工作的最简单方法是从 JNI_OnLoad 函数中调用它。该函数将在加载时在正确的线程中由 JNI 自动调用。旧版本的通知客户端示例在提交 #6500083 之前使用此方法。这是 androidjnibindings.cpp 文件相关部分的摘要:

#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.

这不在 5.2 示例代码中,因为从 C++ 代码调用 Java 不再需要它,但我不知道 QT5.2 示例显示从 Java 调用 C。

回答by user3493496

jniMathClassID = qjniEnv->FindClass("android/src/org/app/test/JniMath");

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

而不是“android/src/org/app/test/JniMath”尝试给出包名:“org/app/test/JniMath”并将其放入

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...

像这样的东西...

回答by Dany19

I used this example http://www.gnuton.org/blog/2014/01/invoking-qtc-code-from-the-java-side-of-qt-for-android-application/

我使用了这个例子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):

我就是这样做的。在我的 java 主类中,我添加了一个静态方法来调用相同的活动(因为它必须是静态的):

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:

在我的 cpp 文件中,我只是像这样调用了我的 java 方法:

void FacebookAndroid::startAndroidFacebook() {
QAndroidJniObject::callStaticMethod<void>("org.qtproject.example.MainActivity",
                                          "startFacebookActivity",
                                          "()V");