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

Posted

技术标签:

【中文标题】使用 Qt/C++ 通过 JNI 调用 Java 代码。 FindClass 找不到类【英文标题】:Using Qt/C++ to call Java code through JNI. FindClass does not find class 【发布时间】:2013-12-31 03:39:01 【问题描述】:

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

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

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

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

错误信息:

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 类:

package org.app.test;
public class JniMath 

    public JniMath()
    

    

    public int eleven()
    
        return 11;
    

.pro 文件:

# 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

ma​​in.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;

项目结构:

编辑:

我还尝试了导致相同错误的不同方法:

QAndroidJniObject *myJavaClass = new QAndroidJniObject("android/src/org/app/test/JniMath");

if(myJavaClass->isValid())

    qDebug() << "Class found!";

else

    qDebug() << "Class NOT found!";

当尝试加载 java/lang/String 时,上面的两个方法都找到了类。

编辑:

错误日志:

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

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

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

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

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

【问题讨论】:

@AlexCohn:我正在寻找一种 Qt 方式来执行此操作,因为现在 QAndroidJniObject 和 QAndroidJniEnvironment 类可用。我还没有找到任何这样做的好例子。正如我所提到的,我以前根本没有使用过 JNI,所以这对我来说是全新的。 很遗憾,您的问题超出了 Qt 模型... 我现在相信您的问题与链接的文章无关。您的问题不是您从辅助线程尝试 JNI,而是您必须设置 Qt Android 应用程序,以便它实际上知道您要使用的 Java 类。如果您查看sample project,您会看到涉及到文件 AndroidManifest.xml。对不起,这超出了我的专业范围。 在您的允许下,我将删除我现在认为与您的事业完全无关的“答案”。 @AlexCohn:我同意。我在做我想做的事方面取得了一些进展。我会更新我的问题并在完成后给出答案。 【参考方案1】:

我用这个例子http://www.gnuton.org/blog/2014/01/invoking-qtc-code-from-the-java-side-of-qt-for-android-application/

我就是这样做的。 在我的 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();
    

然后在我的头文件中添加了这个:

 class FacebookAndroid : public QObject 
    Q_OBJECT

    public:
        FacebookAndroid(QObject *parent = 0);

    public slots:
        void startAndroidFacebook();
    ;

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

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

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

让 FindClass 工作的最简单方法是从 JNI_OnLoad 函数中调用它。 JNI 会在加载时在正确的线程中自动调用此函数。旧版本的 notificationclient 示例在提交 #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;

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

【讨论】:

【参考方案4】:

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

尝试给出包名:“org/app/test/JniMath”,而不是“android/src/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);

这样的……

【讨论】:

以上是关于使用 Qt/C++ 通过 JNI 调用 Java 代码。 FindClass 找不到类的主要内容,如果未能解决你的问题,请参考以下文章

JNI的基本使用一

JNI的基本使用一

JNI的基本使用一

java native interface JNI 调用Java方法

蓝牙与 Android 上的 Qt。通过抽象类上的jni调用java类

C通过JNI反向调用JAVA程序方法