Android サービス
Qt 5.7から、Qtを使ってAndroidサービスを作成できるようになりました。サービスはバックグラウンドで動作するコンポーネントで、ユーザーインターフェイスを持ちません。GPSのログを取ったり、ソーシャルメディアの通知を待ったりなど、長期的な処理を行うのに便利です。サービスは、それを起動したアプリケーションが終了しても実行され続けます。
サービスを組み立てる
始めるには、Qt Creatorの指示に従ってAndroidパッケージディレクトリを作成します:Qt Creator:Deploying Applications to Android Devices で説明されているように、Android パッケージディレクトリを作成します。このディレクトリにはAndroidManifest.xml
ファイルが含まれます。packageディレクトリの中に、src
ディレクトリを作成します。ここには、Javaパッケージとクラスがすべて作成されます。
サービス・クラスの作成
QtService
、またはAndroid.Serviceクラスを拡張して、サービスを作成できます:サービスを作成するには、 または Android: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を使っても、どちらも可能です。
オンデマンドでサービスを開始する
以下の方法でサービスを開始できます:
- QAndroidIntent 、QJniObject 、サービス・インテントを作成し、アプリのメイン・アクティビティ・メソッド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
クラスを使用しても機能しない場合があります。詳細については、ForegroundサービスまたはJobIntentServiceのいずれかを使用するAndroidの推奨を参照してください。
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) { // code to handle main activity execution } else if (argc > 1 && strcmp(argv[1], "-service") == 0) { qDebug() << "Service starting with from the same .so file"; QAndroidService app(argc, argv); return app.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> int main(int argc, char *argv[]) { qWarning() << "Service starting from a separate .so file"; QAndroidService app(argc, argv); return app.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からのネイティブC++コールまたはAndroid BroadcastReceiverを使用できます。
Javaサービスからのネイティブ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 リモートオブジェクトを使う
Qtリモートオブジェクトは、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> int main(int argc, char *argv[]) { qWarning() << "QtAndroidService starting from separate .so"; QAndroidService app(argc, argv); QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica"))); ServiceMessenger serviceMessenger; srcNode.enableRemoting(&serviceMessenger); return app.exec(); }
そして、アプリケーションのmain()
関数で、ソースノードに接続します:
QRemoteObjectNode repNode; repNode.connectToNode(QUrl(QStringLiteral("local:replica"))); QSharedPointer<ServiceMessengerReplica> rep(repNode.acquire<ServiceMessengerReplica>()); bool res = rep->waitForSource(); Q_ASSERT(res); QObject::connect(rep.data(), &ServiceMessengerReplica::pong, [](const QString &message){ qDebug() << "Service sent: " << message; }); rep->ping("Qt and Android are friends!");
この例では、メインアプリケーションのプロセスからサービスにメッセージを送信します。この例では、メイン・アプリケーションのプロセスからサービスにメッセージを送信します。サービスは同じメッセージを返信し、デバッグ・ログに表示されます。
注: 同じ.so
lib ファイルを使用する場合、同じ方法を使用できます。詳しくはUse the same .so Lib Fileをご覧ください。
QAndroidBinderの使用
QAndroidBinder は、Androidで最も重要なメソッドであるBinderを実装することで、プロセス間通信を可能にする便利なクラスです。プロセス間で または オブジェクトを送信できます。QByteArray QVariant
注意: Qt for Android では、1つのプロセスで複数のサービスを実行する場合、一度に1つのサービスしか実行できないという制限があります。そのため、各サービスは個別のプロセスで実行することを推奨します。詳細はQTBUG-78009 を参照してください。
©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。