アンドロイドサービス

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

サービスの組み立て

はじめに、QtをAndroid機能で拡張するで説明したように、Androidパッケージのディレクトリを作成します。このディレクトリにはAndroidManifest.xml ファイルが含まれます。packageディレクトリの中に、src ディレクトリを作成します。このディレクトリには、すべてのJavaパッケージとクラスが作成されます。

サービス・クラスの作成

QtServiceAndroid Serviceのどちらを使用するかを決定する場合、Activityと同じ理由が適用されます。Qtのネイティブ・コールやイベント処理など、Qtライブラリをロードする必要がある機能を使用しない限り、Service

QtService 、または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++から直接サービスを開始できます:
    // Outside of the function body
    Q_DECLARE_JNI_CLASS(Intent, "android/content/Intent")
    Q_DECLARE_JNI_CLASS(ComponentName, "android/content/ComponentName")
    Q_DECLARE_JNI_CLASS(QtAndroidService, "org/qtproject/example/qtandroidservice/QtAndroidService")
    
    // Inside function body
    using namespace QtJniTypes;
    using namespace QNativeInterface;
    
    auto *androidApp = qGuiApp->nativeInterface<QAndroidApplication>();
    Q_ASSERT(androidApp);
    Context context = androidApp->context();
    
    QJniEnvironment env;
    auto serviceClass = env.findClass(Traits<QtAndroidService>::className());
    Intent serviceIntent(context, serviceClass);
    context.callMethod<ComponentName>("startService", serviceIntent);
  • Javaメソッドを呼び出してサービスを開始します。最も簡単な方法は、サービス・クラスに静的メソッドを作成することです:
    public static void startQtAndroidService(Context context) {
            context.startService(new Intent(context, QtAndroidService.class));
    }

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

    using namespace QtJniTypes;
    using namespace QNativeInterface;
    // ...
    auto *androidApp = qGuiApp->nativeInterface<QAndroidApplication>();
    Q_ASSERT(androidApp);
    Context context = androidApp->context();
    QtAndroidService::callStaticMethod<void>("startQtAndroidService", context);

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

ブート時にサービスを実行するには、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 15.0では、BOOT_COMPLETEDを受信した後のフォアグラウンド・サービスの起動にいくつかの制限が導入された。詳細については、BOOT_COMPLETEDの制限を参照してください。
  • Android 8.0では、バックグラウンド・サービスの実行にいくつかの制限が導入されたため、通常のService クラスを使用しても動作しない可能性があります。詳細については、Androidが推奨するForegroundサービスまたはJobIntentServiceを参照してください。

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

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

  • Service を拡張する場合は、サービス・セクションを通常のAndroidサービスとして宣言するだけです。<application>
    <service android:name=".QtAndroidService" android:exported="true">
        <meta-data android:name="android.app.background_running" android:value="true"/>
    </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() 関数でアプリケーションの実行を処理できます。次の引数宣言をサービス本体に追加してください:

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

次に、サービス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 テンプレートを持つサブプロジェクトを用意する必要があります。サンプル・プロジェクトを以下に示します:

  • CMakeで
    find_package(Qt6 REQUIRED COMPONENTS Core)
    
    qt_add_library(service SHARED
        servicemessenger.h
        service_main.cpp
    )
    
    target_link_libraries(service
        PRIVATE
            Qt::Core
            Qt::CorePrivate
    )
  • qmakeでは
    TEMPLATE = lib
    TARGET = service
    CONFIG += dll
    QT += core core-private
    
    SOURCES += \
        service_main.cpp
    
    HEADERS += servicemessenger.h

service_main.cpp 、以下のようになります:

#include <QDebug>#include <QAndroidService>#include <QtCore/private/qandroidextras_p.h>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 for 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(message);
                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 ファイルをメインアプリケーションとメインアプリケーションのサービスの両方に追加します。

  • CMakeでは
    find_package(Qt6 REQUIRED COMPONENTS RemoteObjects)
    
    qt_add_repc_replicas(service
        ../servicemessenger.rep
    )
    
    target_link_libraries(service PRIVATE Qt6::RemoteObjects)
  • qmakeでは
    QT += remoteobjects
    REPC_REPLICA += servicemessenger.rep

サービスサブプロジェクトでは

  • CMakeでは
    find_package(Qt6 REQUIRED COMPONENTS RemoteObjects)
    
    qt_add_repc_sources(service
        ../servicemessenger.rep
    )
    target_link_libraries(service PRIVATE Qt6::RemoteObjects)
  • qmakeでは
    QT += remoteobjects
    REPC_SOURCE += servicemessenger.rep

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

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

#include "servicemessenger.h"#include <QDebug>#include <QAndroidService>#include <QtCore/private/qandroidextras_p.h>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:replica")));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 は、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.