0

QtCreator を使用して C++/Java アプリケーションを .NET にデプロイしていAndroidます。しかし、私の問題は、QtCreator がアプリを展開する方法に固有のものではない可能性があると思います。

特定の機能を提供する C++ ライブラリを作成したいと考えています。そのためには、ライブラリで Java クラスをインスタンス化する必要があります。この最後のクラスは、SDK 関数クラス (NDK/C++ API で利用できないもの) を実行するために使用されます。

C++ プログラムからの Java オブジェクトの作成と使用は正常に機能します。コンパイル/デプロイ中に .java ファイルをアプリケーション環境にパッケージ化すると、次の 2 つの方法で Java クラスを使用できます。

  • を宣言JNI_OnLoadし、クラス ID、メソッド ID をロードし、後で jni を使用してそれらを呼び出します
  • QtQAndroidJniObjectオブジェクトを使用する (これは 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
4

1 に答える 1

1

最後に、これを解決する方法を得ました。

jniload.pro ファイルから 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-build/src に FooLib.java と FooPrg.java の両方が含まれ、ライブラリとプログラムの両方がそれらにアクセスできるようになります!

于 2015-04-02T09:26:09.090 に答える