Android Studioで作成したJarファイルをQt/Androidアプリで使用する


はじめに

Android Studioで作成したJavaクラスをJarファイル化し、JarファイルをQtプロジェクトに取り込んで、Qt/AndroidアプリからJarファイルのクラスを使用する方法を紹介します。

開発環境

  • Android Studio:3.5.3
  • Android端末:SO-02J(Android8.0.0)
  • Qt Creator:4.13.3
  • コンパイラ:Android Qt 5.15.2 Clang Multi-Abi

Android StudioでJarファイルを作成する

Android StudioにはJarファイル単体を作成するプロジェクトはない為、まず使い捨ての空プロジェクトを作成します。

File -> New -> New Moduleを選択します。

Java Libraryを選択してNextを押下します。


Library nameをmyLibraryに変更し、Finishを押下します。


すると、ツリーにmyLibraryが追加され、MyClass.javaが作成されます。
MyClassに次のようにgetWord()メソッドを追加します。

MyClass.java
package com.argama147.mylibrary;

public class MyClass {
    public String getWord() {
        return "test";
    }
}

View -> Tool Windows -> Gradleを選択します。


Gradleウィンドウで、myLibrary -> Tasks -> buildにあるjarをダブルクリックします。
するとプロジェクトディレクトリ/myLibrary/build/libs/にmyLibrary.jarが作成されます。

作成されたライブラリの中身を確認

Jarファイルはいくつかのコマンドを使用することで逆アセンブルできますが、http://java-decompiler.github.io/ のJD-GUIというツールを使用すれば簡単に中身を確認することができます。

Qt/Androidアプリ作成

Qt CreatorでQt Quickアプリを作成します。サンプルはGitHubに格納してあります。


プロジェクトテンプレートでQt Quickアプリを作成します。

Android用の設定

プロジェクトファイル

UsrJar.pro
QT += quick androidextras

プロジェクトファイルにandroidextrasを追加します。

Android固有ファイルを追加

プロジェクト -> Build Android APK -> Application にあるCreate Templatesボタンを押下します。
すると、プロジェクトディレクトリ直下にandroidディレクトリが作成され、AndroidManifest.xml等のファイルが作成されます。

Jarファイルのコピー

Jarファイルは$ANDROID_PACKAGE_SOURCE_DIR/libs下に格納すると認識されます。先程のCreate Templatesの操作で、プロジェクトファイルに

UsrJar.pro
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android

のように追加されていますので、プロジェクトディレクトリ/android/下にlibsディレクトリを作成し、Android Studioで作成したmyLibrary.jarをコピーします。

QMLコードを改造

Javaクラスで取得した文字列を画面に表示したい為、main.qmlを次のように改造します。

main.qml
import QtQuick 2.15;
import QtQuick.Controls 2.15;
import QtQuick.Layouts 1.15;

ApplicationWindow {
    id: window
    visible: true

    header: ToolBar {
        RowLayout {
            id: rowlayout
            anchors.fill: parent
            Label {
                text: "JarSample"
                font.pixelSize: 20
                color: "white"

                anchors.right: menu.right
                anchors.left: parent.left
                Layout.alignment: Qt.AlignTop | Qt.AlignBottom
                horizontalAlignment: Qt.AlignHCenter
                verticalAlignment: Qt.AlignVCenter
                Layout.fillWidth: true
                Layout.fillHeight: true
                background: Rectangle {
                    gradient: Gradient {
                        orientation: Gradient.Horizontal
                        GradientStop { position: 0.0; color: "lightsteelblue" }
                        GradientStop { position: 1.0; color: "blue" }
                    }
                }
            }
        }
    }

    StackView {
        id: stackView
        anchors.fill: parent
        initialItem: Pane {
            id: pane
            RowLayout {
                anchors.fill: parent
                Label {
                    objectName: "resultText"
                    horizontalAlignment: Text.AlignLeft
                    verticalAlignment: Text.AlignTop
                    wrapMode: Label.Wrap
                    Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
                }
            }
        }
    }
}

「anchors.fill: parent」の部分で、

のようにM16エラーが出ていますが、動作には問題ありません。MinGWやMSVCコンパイラではエラーが出ない為、Androidコンパイラ固有の問題のようです。
この問題についてはQTBUG-89519で扱っています(QTCREATORBUG-24232で報告したらQTBUG-89519のreopenの形になった)。

C++からJavaクラスを使用する

C++からJNI経由でJavaクラスを使用するコードを追加します。

main.cpp
    // Create MyClass Class Instance
    QAndroidJniObject *instance = new QAndroidJniObject("com/argama147/mylibrary/MyClass");
    // Call Method
    QAndroidJniObject ret = instance->callObjectMethod("getWord","()Ljava/lang/String;");
    QString str = ret.toString();
    qDebug() << "ret" << str;
    delete instance;

    // Update Property
    QObject* root = engine.rootObjects().first();
    QQuickItem *resultText = root->findChild<QQuickItem *>("resultText");
    resultText->setProperty("text", str);

QAndroidJniObjectでJavaのcom.argama147.mylibrary.MyClassクラスを生成し、callObjectMethod()でgetWord()メソッドを呼び出しています。取得した文字列を「resultText」に渡しています。

実行結果


getWord()から取得した「test」が画面に表示されました。