我正在使用QtCreator在Android
上部署C++/Java应用程序。但我认为我的问题可能不是特定于QtCreator部署应用程序的方式。从C++库嵌入java代码而不需要顶级程序来嵌入它?
我想创建一个提供特定功能的C++库。为此,库需要实例化一个Java类,最后一个将用于做一些SDK函数类(对于NDK/C++ API中没有的东西)。
从C++程序创建和使用java对象可以正常工作。我打包.java文件编译/部署过程中的应用环境,然后我可以通过两种技术途径使用Java类:
- 声明
JNI_OnLoad
,负载类ID,方法ID,然后再使用JNI 打电话给他们
- 使用Qt
QAndroidJniObject
对象(这是特定于QtCreator)
现在的问题是当我想创建并从C++库使用Java对象。它只适用于.java文件与顶层应用程序一起打包的情况。我无法找到将Java打包并仅与库本身打包的方法。这意味着任何人为什么需要使用我的库,不仅需要简单地链接库,还需要打包我的库所需的.java文件。这破坏了封装,给最终开发人员编写程序带来了困难,并且只是希望加载一个库,并希望它能够嵌入自己需要的所有工作...
我的问题是:库如何嵌入java文件,这样这个java文件不需要成为顶级程序包的一部分,以便让库使用它?
以下是一个快速示例: MainWindow constrctor自己尝试创建和使用Java对象,调用4个函数。只有前两个电话中工作...
main.cpp中:
#include <QApplication>
#include <QMainWindow>
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava(JNIEnv *env)
{
jclass clazz = env->FindClass("my/FooPrg");
if (!clazz)
{
qCritical("Can't find FooPrg class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID)
{
qCritical("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID)
{
qCritical("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
loadJava(env);
return JNI_VERSION_1_4;
}
void callJavaFunctionFromPrgWithQt()
{
if (QAndroidJniObject::isClassAvailable("my/FooPrg"))
{
QAndroidJniObject obj("my/FooPrg","()V");
if (obj.isValid())
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert(res == 4);
}
else
{
assert(false);
}
}
else
{
assert(false);
}
}
void callJavaFunctionFromPrgWithJniLoad()
{
if (s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0)
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef(env->NewObject(s_classID, s_ctorMethodID));
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002);
assert(res == 4);
}
else
{
assert(false);
}
}
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
callJavaFunctionFromPrgWithQt(); // works
callJavaFunctionFromPrgWithJniLoad(); // works
callJavaFunctionFromLibWithQt(); // fails, assert
callJavaFunctionFromLibWithJniLoad(); // fails, because libraries JNI_OnLoad can't find FooLib.java!
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MyLib.h:
#pragma once
void callJavaFunctionFromLibWithQt();
void callJavaFunctionFromLibWithJniLoad();
MyLib.cpp:
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava(JNIEnv *env)
{
jclass clazz = env->FindClass("my/FooLib");
if (!clazz)
{
qDebug("Can't find FooLib class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID)
{
qDebug("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID)
{
qDebug("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
// uncommenting this makes the application crash upon load....
//loadJava(env);
return JNI_VERSION_1_4;
}
void callJavaFunctionFromLibWithQt()
{
if (QAndroidJniObject::isClassAvailable("my/FooLib"))
{
QAndroidJniObject obj("my/FooLib","()V");
if (obj.isValid())
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert(res == 4);
}
else
{
assert(false);
}
}
else
{
assert(false); // this assertion is reached!
}
}
void callJavaFunctionFromLibWithJniLoad()
{
if (s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0)
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef(env->NewObject(s_classID, s_ctorMethodID));
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002);
assert(res == 4);
}
else
{
assert(false); // this assertion is reached!
}
}
FooPrg.java:
package my;
import java.lang.Integer;
public class FooPrg
{
public FooPrg()
{
}
public int Mult(int val)
{
return val * 2;
}
}
FooLib.java:
package my;
import java.lang.Integer;
public class FooLib
{
public FooLib()
{
}
public int Mult(int val)
{
return val * 2;
}
}
jniload.pro:
TARGET = jniload
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/main.cpp
TEMPLATE = app
INCLUDEPATH += ifc
LIBS += \
-l$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_EXTRA_LIBS += \
$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooPrg.java
jniload_lib.pro:
TARGET = jniload_lib
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/MyLib.cpp
HEADERS += ifc/MyLib.h
TEMPLATE = lib
INCLUDEPATH += ifc
# This does has apparently no effect on library
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooLib.java
您是否熟悉Qt资源系统,该系统会将外部文件编译为要从Qt方法中使用的二进制文件?我不知道你正在做的事情,但这听起来像是一个可能的想法。注意:我从来没有将lib资源系统用于应用程序。 – 2015-03-31 16:21:27
我为库和应用程序使用了很多Qt资源。但我不认为在这里存储.java文件将有助于这种特殊情况。除非有办法告诉Java去查找源代码...... – jpo38 2015-03-31 19:09:53