2015-03-31 23 views
0

我正在使用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个函数。只有前两个电话中工作...

enter image description here

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 
+0

您是否熟悉Qt资源系统,该系统会将外部文件编译为要从Qt方法中使用的二进制文件?我不知道你正在做的事情,但这听起来像是一个可能的想法。注意:我从来没有将lib资源系统用于应用程序。 – 2015-03-31 16:21:27

+0

我为库和应用程序使用了很多Qt资源。但我不认为在这里存储.java文件将有助于这种特殊情况。除非有办法告诉Java去查找源代码...... – jpo38 2015-03-31 19:09:53

回答

1

Finaly得到了一个办法来解决这一问题。

我从jniload.pro文件和hanlde手动副本中删除了ANDROID_PACKAGE_SOURCE_DIR行。通过自定义的Java文件生成步骤:

custom_jniload_lib_step.target = jniload_lib_mockup.h 
custom_jniload_lib_step.commands = $(COPY_DIR) data\android\root ..\..\android-build 
QMAKE_EXTRA_TARGETS += custom_jniload_lib_step 
PRE_TARGETDEPS += jniload_lib_mockup.h 

custom_jniload_step.target = jniload_mockup.h 
custom_jniload_step.commands = $(COPY_DIR) data\android\root ..\..\android-build 
QMAKE_EXTRA_TARGETS += custom_jniload_step 
PRE_TARGETDEPS += jniload_mockup.h 

然后,在部署时,Android系统的构建/ src目录既包含FooLib.java和FooPrg.java然后双方库和程序可以访问它们!