Android 서비스
Qt를 사용하여 안드로이드 서비스를 생성할 수 있습니다. 서비스는 백그라운드에서 실행되는 구성 요소이므로 사용자 인터페이스가 없습니다. GPS 기록, 소셜 미디어 알림 대기 등과 같은 장시간 지속되는 작업을 수행하는 데 유용합니다. 서비스는 이를 시작한 애플리케이션이 종료되더라도 계속 실행됩니다.
서비스 구성
시작하려면 ‘Extending Qt with Android Facilities’의 지침에 따라 Android 패키지 디렉터리를 생성하십시오. 이 디렉터리에는 ` AndroidManifest.xml ` 파일이 포함되어 있습니다. 패키지 디렉터리 내에 ` src ` 디렉터리를 생성하십시오. 이곳에 모든 Java 패키지와 클래스가 생성될 것입니다.
서비스 클래스 생성
QtService 와 Android 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를 사용하여 두 가지 방법 모두 구현할 수 있습니다.
필요 시 서비스 시작
다음과 같은 방법으로 서비스를 시작할 수 있습니다:
- QAndroidIntent 및 QJniObject 를 사용하여 C++에서 직접, 서비스 인텐트를 생성하고 앱의 메인 액티비티 메서드인 startService()를 호출하여:
// 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에서 권장하는 ‘포그라운드 서비스’ 또는 ‘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 라이브러리가 로드되지 않으므로 통신을 위한 네이티브 호출을 전혀 사용할 수 없습니다. 별도의 프로세스에서 실행하려면 서비스 태그에 다음을 추가하십시오:android:process=":qt_service"QtService를 확장할 때는 Qt XML에 필요한 모든 라이브러리를 로드하기 위해 다른 항목을 선언해야 합니다. 주로QtActivity의<activity>섹션에 있는 항목과 동일합니다. 다음 내용을 추가하십시오:<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() ` 함수를 호출합니다. 별도의 프로세스에서 실행할 때는 메인 액티비티와 동일한 라이브러리 파일을 사용하거나 별도의 라이브러리 파일을 사용하여 서비스를 시작할 수 있습니다.
동일한 .so 라이브러리 파일 사용
메인 액티비티와 동일한 .so 라이브러리 파일을 사용하면, 서비스는 메인 액티비티와 구별하기 위해 추가 인수를 포함한 동일한 진입점을 사용하게 됩니다. 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 라이브러리 파일을 사용할 경우, 애플리케이션의 main() 함수가 두 번 실행됩니다. 첫 번째는 메인 액티비티를 시작할 때이고, 두 번째는 서비스를 시작할 때입니다. 따라서 제공된 인자에 따라 각 실행을 처리해야 합니다. 이를 구현하는 한 가지 방법은 다음과 같습니다:
if (argc <= 1) {
// 메인 액티비티 실행을 처리하는 코드
} 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 템플릿을 사용하는 하위 프로젝트가 필요합니다. 샘플 프로젝트는 다음과 같습니다:
- 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>
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 서비스에서 네이티브 C++ 호출을 사용하거나 Android BroadcastReceiver를 사용할 수 있습니다.
Java 서비스에서 네이티브 C++ 호출
이 방법은 QtActivity 와 동일한 프로세스에서 실행되는 서비스와 연동될 수 있으며, Service 를 상속한 경우에도 작동합니다.
자세한 내용은 ‘Qt for Android Notifier 예제’를 참조하십시오.
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()`를 호출하여 "simple_string" 로 Qt에 메시지를 전송합니다:
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);
}
}
};
}이 모든 기능을 활용하려면 ‘서비스 시작’ 섹션에 설명된 대로 서비스를 시작한 다음, 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 ` 라이브러리 파일이 있는 서비스 예제를 살펴보겠습니다. 통신 클래스를 정의하는 ` .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>
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와 Android는 친구입니다!");이 예제는 메인 애플리케이션의 프로세스에서 서비스로 메시지를 전송합니다. 서비스는 동일한 메시지로 응답하며, 이 메시지는 디버그 로그캣에 출력됩니다.
참고: 동일한 .so 라이브러리 파일을 사용할 때도이 방법을 사용할 수 있습니다. 자세한 내용은 ‘동일한 .so 라이브러리 파일 사용’을 참조하십시오.
QAndroidBinder 사용
QAndroidBinder 는 Binder의 가장 중요한 메서드들을 구현하여 프로세스 간 통신을 가능하게 하는 편의 클래스입니다. 이를 통해 프로세스 간에 QByteArray 또는 QVariant 객체를 전송할 수 있습니다.
참고: Qt for Android에는 하나의 프로세스에서 여러 서비스를 실행할 때 한 번에 하나의 서비스만 실행하도록 제한하는 기능이 있습니다. 따라서 각 서비스를 별도의 프로세스에서 실행하는 것이 권장됩니다. 자세한 내용은 QTBUG-78009를 참조하십시오.
© 2026 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.