Android — Вставить Java-код из библиотеки C ++ без необходимости программы верхнего уровня для его внедрения?

Я использую QtCreator для развертывания приложений C ++ / Java на Android, Но я думаю, что моя проблема может быть не связана с тем, как QtCreator развертывает приложение.

Я хочу создать библиотеку C ++, обеспечивающую конкретную функциональность. Для этого библиотека должна создать экземпляр класса Java, последний будет использоваться для выполнения некоторых функций класса SDK (для вещей, которые недоступны в API NDK / C ++).

Создание и использование объектов Java из программы на C ++ работает нормально. Я упаковываю файл .java в среду приложения во время компиляции / развертывания и затем могу использовать класс Java двумя способами:

  • декларировать JNI_OnLoad, загрузить идентификатор класса, идентификатор метода, а затем вызывать их, используя jni
  • Используйте Qt QAndroidJniObject объекты (это специфично для QtCreator)

Теперь проблема возникает, когда я хочу создать и использовать объекты Java из библиотеки C ++. Это работает, только если файл .java упакован с приложением верхнего уровня. Я не мог найти способ упаковать Java только с самой библиотекой. Это означает, что любой, кому необходимо использовать мою библиотеку, должен будет не только просто связать ее с библиотекой, но и упаковать файлы .java, необходимые для моей библиотеки. Это нарушает инкапсуляцию и создает трудности для конечного разработчика, пишущего программы, и просто желающего загрузить библиотеку и ожидающего, что она внедрит все необходимое для самостоятельной работы …

Мой вопрос: как библиотека может встраивать java-файл, чтобы этот java-файл не обязательно был частью пакета программ верхнего уровня, чтобы библиотека могла его использовать?

Вот быстрый пример: MainWindow constrctor сам вызывает 4 функции, пытаясь создать и использовать объекты Java. Работают только первые два звонка …

введите описание изображения здесь

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

Решение

Наконец-то есть способ решить это.

Я удалил строку ANDROID_PACKAGE_SOURCE_DIR из файла jniload.pro и вручную скопировал файлы .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-build / src содержит и FooLib.java, и FooPrg.java, и тогда и библиотека, и программа могут получить к ним доступ!

1

Другие решения


По вопросам рекламы [email protected]