Androidサービス

Qt 5.7から、Qtを使ってAndroidサービスを作成できるようになりました。サービスはバックグラウンドで動作するコンポーネントで、ユーザーインターフェイスを持ちません。GPSのログを取ったり、ソーシャルメディアの通知を待ったりなど、長期的な処理を行うのに便利です。サービスは、それを起動したアプリケーションが終了しても実行され続けます。

サービスを組み立てる

始めるには、Qt Creator の指示に従ってAndroidパッケージ・ディレクトリを作成します:Androidデバイスにアプリケーションをデプロイする。このディレクトリには、AndroidManifest.xml ファイルが含まれます。packageディレクトリの中に、src ディレクトリを作成します。ここには、Javaパッケージとクラスがすべて作成されます。

サービス・クラスの作成

QtService 、またはAndroid.Serviceというクラスを拡張することで、サービスを作成できます:Serviceクラスを Java クラスに拡張することでサービスを作成できます。サービス内でQtの機能を使用するか、JavaからネイティブC++関数を呼び出すかによって、QtService またはService のどちらかを拡張する必要があります。簡単なサービスから始めましょう:

import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.qtproject.qt.android.bindings.QtService;

public class QtAndroidService extends QtService
{
    private static final String TAG = "QtAndroidService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Creating Service");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "Destroying Service");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int ret = super.onStartCommand(intent, flags, startId);

        // Do some work

        return ret;
    }
}

サービスを開始する

Androidでは、オンデマンドまたは起動時にサービスを開始することができます。Qtを使ってもその両方が可能です。

オンデマンドでサービスを開始する

以下の方法でサービスを開始できます:

  • QAndroidIntentQJniObject 、サービス・インテントを作成し、アプリのメイン・アクティビティ・メソッドstartService()を呼び出すことで、C++から直接サービスを開始できます:
    auto activity = QJniObject(QNativeInterface::QAndroidApplication::context());
    QAndroidIntent serviceIntent(activity.object(),
                                 "org/qtproject/example/qtandroidservice/QtAndroidService");
    QJniObject result = activity.callObjectMethod(
                "startService",
                "(Landroid/content/Intent;)Landroid/content/ComponentName;",
                serviceIntent.handle().object());
  • Javaメソッドを呼び出してサービスを開始します。最も簡単な方法は、サービス・クラスに静的メソッドを作成することです:
    public static void startQtAndroidService(Context context) {
            context.startService(new Intent(context, QtAndroidService.class));
    }

    次のJNIコールを使用して、C++からこのメソッドを呼び出すことができます:

    const QJniObject context(QNativeInterface::QAndroidApplication::context());
    QJniObject::callStaticMethod<void>(
        "org/qtproject/example/qtandroidservice/QtAndroidService",
        "startQtAndroidService",
        "(Landroid/content/Context;)V",
        context.object());

ブート時にサービスを開始する

ブート時にサービスを実行するには、BroadcastReceiverが必要です。

カスタム Java クラスを作成します:

public class QtBootServiceBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, QtAndroidService.class);
        context.startService(startServiceIntent);
    }
}

AndroidManifest.xml ファイルの<manifest> セクションの本文に、以下のuses-permission を追加します:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

また、<application> セクションの本文にreceiver の定義を追加する:

<receiver android:name=".QtBootServiceBroadcastReceiver" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

注: Android 8.0では、バックグラウンド・サービスの実行にいくつかの制限が導入されたため、通常のService クラスを使用しても動作しない場合があります。詳細については、Androidが推奨するForegroundサービスまたはJobIntentServiceを参照してください。

AndroidManifest.xmlでサービスを管理する

サービスをAndroidアプリで使えるようにするには、AndroidManifest.xml ファイルで宣言する必要があります。まずは、サービス・セクションを追加するところから始めましょう:

  • Service を拡張する場合は、サービス・セクションを通常のAndroidサービスとして宣言するだけです。<application>
    <service android:name=".QtAndroidService" android:exported="true">
        <!-- Background running -->
        <meta-data android:name="android.app.background_running" android:value="true"/>
        <!-- Background running -->
    </service>

    こうすることで、サービスがQtActivity と同じプロセスで開始され、Java コードからネイティブ C++ 呼び出しを使用できるようになります。別のプロセスで実行することもできますが、その場合はQtライブラリーがロードされないため、通信にネイティブ・コールを使用できません。別プロセスで実行するには、serviceタグに次のように追加します:

    android:process=":qt_service"
  • QtService を拡張する場合、Qt に必要なすべてのライブラリをロードするための他の項目を宣言する必要があります。主に、<activity> のセクションと同じ項目をQtActivity に宣言します。以下を追加してください:
    <service android:process=":qt_service" android:name=".QtAndroidService" android:exported="true">
        <meta-data android:name="android.app.lib_name" android:value="service"/>
        <meta-data android:name="android.app.background_running" android:value="true"/>
    </service>

注意: サービスをバックグラウンドで実行するために、以下を必ず定義してください:

<meta-data android:name="android.app.background_running" android:value="true"/>

サービスを宣言する方法にはいくつかのバリエーションがあります。そのうちのいくつかは、前のマニフェストのスニペットですでに使用されています。使用するケースに応じて、サービスを QtActivity と同じプロセスで実行するか、別のプロセスで実行します。

QtActivity と同じプロセス内のサービス

QtActivity と同じプロセスでサービスを実行するには、以下のようにサービスヘッダを宣言します:

<service android:name=".QtAndroidService" android:exported="true">

別プロセスのサービス

サービスを専用プロセスで実行するには、サービスヘッダを次のように宣言します:

<service android:process=":qt_service" android:name=".QtAndroidService" android:exported="true">

Qt はandroid.app.lib_name meta-data で定義された.so ファイルをロードし、android.app.arguments meta-data で設定されたすべての引数でmain() 関数を呼び出します。別のプロセスで実行する場合、メイン・アクティビティと同じlibファイルまたは別のlibファイルを使用してサービスを開始することができます。

同じ.so libファイルを使用する

メイン・アクティビティと同じ.so libファイルを使用することは、サービスがメイン・アクティビティと区別するための余分な引数を持つ同じエントリ・ポイントを使用することを意味します。提供された引数に従って、main() 関数でアプリケーションの実行を処理できます。次の引数宣言をサービス本体に追加してください:

<!-- Application arguments -->
<meta-data android:name="android.app.arguments" android:value="-service"/>
<!-- Application arguments -->

次に、サービスandroid.app.lib_name がメイン・アクティビティと同じであることを確認し、以下を追加します:

<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>

同じ.so libファイルを使用する場合、アプリケーションのmain() 関数は2回実行されます。1回目はメイン・アクティビティを開始するため、2回目はサービスを開始するためです。したがって、提供された引数に従って、それぞれの実行を処理する必要があります。そのための1つの方法は以下の通りです:

if(argc<= 1) {// メイン・アクティビティの実行を処理するコード}else if(argc> 1 &&strcmp(argv[1], "-service")== 0) {if(argc> 1 &&strcmp(argv[1], "-service")== 0)    qDebug() << "Service starting with from the same .so file";
    QAndroidServiceapp(argc,argv);returnapp.exec(); }else{.    qWarning() << "Unrecognized command line argument";
   return -1; }
別の.so Libファイルを使う

この場合、サービスに別の実行ファイルを提供するlib テンプレートを持つサブプロジェクトを用意する必要があります。サンプル・プロジェクト.pro

TEMPLATE = lib
TARGET = service
CONFIG += dll
QT += core core-private

SOURCES += \
    service_main.cpp

HEADERS += servicemessenger.h

service_main.cpp

#include <QDebug>#include <QAndroidService>intmain(intargc, char *argv[]){
    qWarning() << "Service starting from a separate .so file";
    QAndroidServiceapp(argc,argv);returnapp.exec(); }

AndroidManifest.xml で、サービスのandroid.app.lib_name を定義する:

<meta-data android:name="android.app.lib_name" android:value="service"/>

サービスとの通信

Qt for Androidは、Androidサービスと通信するための様々なプロセス間通信(IPC)メソッドを提供しています。プロジェクトの構造に応じて、Java ServiceまたはAndroid BroadcastReceiverからのネイティブC++コールを使用できます。

Java ServiceからのネイティブC++コール

これは、QtActivity と同じプロセスで実行されているサービスや、Service が拡張されている場合でも機能します。

詳細については、Qt Android Notifier Exampleを参照してください。

Android BroadcastReceiverの使用

Android BroadcastReceiverを使用すると、Androidシステム、アプリ、アクティビティ、サービス間でメッセージを交換できます。他の Android 機能と同様に、Qt でもブロードキャストレシーバーを使用して、QtActivity とサービス間でメッセージを交換できます。まず、サービスからメッセージを送信するロジックから説明します。sendBroadcast()を呼び出すサービスの実装に以下を追加します:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    int ret = super.onStartCommand(intent, flags, startId);

    Intent sendToUiIntent = new Intent();
    sendToUiIntent.setAction(ActivityUtils.BROADCAST_CUSTOM_ACTION);
    sendToUiIntent.putExtra("message", "simple_string");

    Log.i(TAG, "Service sending broadcast");
    sendBroadcast(sendToUiIntent);

    return ret;
}

次に、Qtのメイン・アクティビティからブロードキャスト・レシーバーを作成し、登録する必要があります。最も簡単な方法は、メソッドを持つカスタムクラスを作成し、Javaですべてのロジックを実装することです。次の例では、ネイティブ・メソッドsendToQt() を呼び出して、Qt にメッセージ"simple_string" を送信しています:

public class ServiceBroadcastUtils {

    private static native void sendToQt(String message);

    private static final String TAG = "ActivityUtils";
    public static final String BROADCAST_CUSTOM_ACTION = "org.qtproject.example.qtandroidservice.broadcast.custom";

    public void registerServiceBroadcastReceiver(Context context) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BROADCAST_CUSTOM_ACTION);
        context.registerReceiver(serviceMessageReceiver, intentFilter);
        Log.i(TAG, "Registered broadcast receiver");
    }

    private BroadcastReceiver serviceMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "In OnReceive()");
            if (BROADCAST_CUSTOM_ACTION.equals(intent.getAction())) {
                String message = intent.getStringExtra("message");
                sendToQt(data);
                Log.i(TAG, "Service sent back message to C++: " + message);
            }
        }
    };
}

これらすべてを利用するには、Start the Serviceで示したようにサービスを開始し、registerServiceBroadcastReceiver() メソッドを呼び出してブロードキャスト・レシーバーを登録します:

QJniEnvironment env;
jclass javaClass = env.findClass("org/qtproject/example/qtandroidservice/ActivityUtils");
QJniObject classObject(javaClass);
const QJniObject context(QNativeInterface::QAndroidApplication::context());
classObject.callMethod<void>("registerServiceBroadcastReceiver",
                             "(Landroid/content/Context;)V",
                             context.object());

メソッドQt Remote Objects

Qt Remote ObjectsはQtプロセス間でAPIを共有する簡単な方法を提供します。主なコンセプトは、サービスプロセスにサーバーを置き、Qtアプリケーションにレプリカを置くことです。

レプリカの準備

個別の.so libファイルを持つサービスの例を考えてみましょう。通信クラスを定義する.rep

class ServiceMessenger {
    SLOT(void ping(const QString &message));
    SIGNAL(pong(const QString &message));
}

サービス・サブプロジェクトでは、このクラスをservicemessenger.h として定義します:

#include "rep_servicemessenger_source.h"

class ServiceMessenger : public ServiceMessengerSource {
public slots:
    void ping(const QString &name) override {
        emit pong("Hello " + name);
    }
};

次に、.rep ファイルをメインアプリケーションの.pro ファイルとサービスの ファイルの両方に追加します:

QT += remoteobjects
REPC_REPLICA += servicemessenger.rep

そして、サービスサブプロジェクトに

QT += remoteobjects
REPC_SOURCE += servicemessenger.rep

ソースとレプリカを接続する

サービス・サブプロジェクトのmain() 関数で、Qt Remote Objects ソース・ノードを定義する:

#include "servicemessenger.h"#include <QDebug>#include <QAndroidService>intmain(intargc, char *argv[]){
    qWarning() << "QtAndroidService starting from separate .so";
    QAndroidServiceapp(argc,argv);    QRemoteObjectHostsrcNode(QUrl(QStringLiteral("local:replica")); ServiceMessenger serviceMessenger; srcNode.enableRemoting(&serviceMessenger);returnapp.exec(); }

次に、アプリケーションのmain() 関数で、ソース・ノードに接続します:

QRemoteObjectNoderepNode; repNode.connectToNode(QUrl(QStringLiteral("local:レプリカ"));QSharedPointer<ServiceMessengerReplica>rep(repNode.acquire<ServiceMessengerReplica>());boolres=  rep->waitForSource(); Q_ASSERT(res);QObject::connect(rep.data(), &ServiceMessengerReplica::pong, [](constQStringメッセージ    qDebug() << "Service sent: " << message;
}); rep->ping("Qt と Android は友達です!");

この例では、メイン・アプリケーションのプロセスからサービスにメッセージを送信しています。サービスは同じメッセージを返信し、デバッグログに出力されます。

注: 同じ.so libファイルを使用する場合、同じ方法が使用できます。詳細については、「同じ.so Libファイルを使用する」を参照してください。

QAndroidBinderを使う

QAndroidBinder は、Androidで最も重要なメソッドであるBinderを実装することで、プロセス間通信を可能にする便利なクラスです。プロセス間で または オブジェクトを送信できます。QByteArray QVariant

注意: Qt for Androidでは、1つのプロセスで複数のサービスを実行する場合、一度に1つのサービスしか実行できないという制限があります。そのため、各サービスは個別のプロセスで実行することをお勧めします。詳細はQTBUG-78009 を参照してください。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.