Help us understand the problem. What is going on with this article?

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ファイル単体を作成するプロジェクトはない為、まず使い捨ての空プロジェクトを作成します。
Android1.png

File -> New -> New Moduleを選択します。
Android2.png
Java Libraryを選択してNextを押下します。

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

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

MyClass.java
package com.argama147.mylibrary;

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

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

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

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

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

jd_gui.png

Qt/Androidアプリ作成

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

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

Android用の設定

プロジェクトファイル

UsrJar.pro
QT += quick androidextras

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

Android固有ファイルを追加

Qt2.png

プロジェクト -> 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」の部分で、
QML_M16error.png
のように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」に渡しています。

実行結果

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

argama147
サークル:エゥーゴ。Qt/C++、Android/Java,Kotlin、IoTを主軸として活動するスキル横伸ばし型フリーランスPG/SE。技術書典5でQt5/C++入門、技術書典6でIoT、技術書典7でQtでAndroidアプリを作る本、技術書典9で「ライブラリを作ろう」という技術同人誌を頒布し、その後商業出版した。
https://techbookfest.org/organization/43220004
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away