QtCreator を使用して C++/Java アプリケーションを .NET にデプロイしていAndroid
ます。しかし、私の問題は、QtCreator がアプリを展開する方法に固有のものではない可能性があると思います。
特定の機能を提供する C++ ライブラリを作成したいと考えています。そのためには、ライブラリで Java クラスをインスタンス化する必要があります。この最後のクラスは、SDK 関数クラス (NDK/C++ API で利用できないもの) を実行するために使用されます。
C++ プログラムからの Java オブジェクトの作成と使用は正常に機能します。コンパイル/デプロイ中に .java ファイルをアプリケーション環境にパッケージ化すると、次の 2 つの方法で Java クラスを使用できます。
- を宣言
JNI_OnLoad
し、クラス ID、メソッド ID をロードし、後で jni を使用してそれらを呼び出します - Qt
QAndroidJniObject
オブジェクトを使用する (これは QtCreator に固有です)
問題は、C++ ライブラリから Java オブジェクトを作成して使用するときに発生します。.java ファイルが最上位アプリケーションにパッケージ化されている場合にのみ機能します。Javaをライブラリ自体とのみパッケージ化する方法が見つかりませんでした。つまり、私のライブラリを使用する必要がある人は、単純にライブラリにリンクするだけでなく、私のライブラリに必要な .java ファイルをパッケージ化する必要があります。これはカプセル化を破り、最終的な開発者がプログラムを作成し、単にライブラリをロードしたいだけで、それが独自に動作するために必要なすべてを埋め込むことを期待するのに苦労します...
私の質問は次のとおりです。ライブラリがJavaファイルを使用できるようにするために、このJavaファイルがトップレベルのプログラムパッケージの一部である必要がないように、ライブラリにJavaファイルを埋め込むにはどうすればよいですか?
簡単なサンプルを次に示します。MainWindow コンストラクターは、4 つの関数を呼び出して、Java オブジェクトを作成および使用しようとします。最初の 2 つの呼び出しのみが機能します...
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