Android 서비스

Qt 5.7부터는 Qt를 사용하여 안드로이드 서비스를 만들 수 있습니다. 서비스는 백그라운드에서 실행되는 컴포넌트이므로 사용자 인터페이스가 없습니다. GPS 로깅, 소셜 미디어 알림 대기 등과 같은 장기적인 작업을 수행하는 데 유용합니다. 서비스는 서비스를 시작한 애플리케이션이 종료되더라도 계속 실행됩니다.

서비스 조립하기

시작하려면 Qt Creator 에 안내된 대로 Android 패키지 디렉토리를 만듭니다: Android 장치에 애플리케이션 배포하기. 이 디렉터리에는 AndroidManifest.xml 파일이 포함됩니다. 패키지 디렉터리 내에 모든 Java 패키지와 클래스가 생성될 src 디렉터리를 만듭니다.

서비스 클래스 생성

QtService 또는 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;
    }
}

서비스 시작하기

안드로이드에서는 온디맨드 또는 부팅 시 서비스를 시작할 수 있습니다. Qt를 사용하여 두 가지를 모두 수행할 수도 있습니다.

온디맨드 서비스 시작

다음과 같은 방법으로 서비스를 시작할 수 있습니다:

  • C++에서 직접 QAndroidIntentQJniObject 을 사용하여 서비스 인텐트를 생성하고 앱의 기본 활동 메서드 startService()를 호출합니다:
    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 클래스를 사용하면 작동하지 않을 수 있습니다. 자세한 내용은 안드로이드에서 권장하는 포그라운드 서비스 또는 잡인텐트서비스 사용을 참조하세요.

AndroidManifest.xml에서 서비스 관리하기

Android 앱에서 서비스를 사용하려면 AndroidManifest.xml 파일에 서비스를 선언해야 합니다. 서비스 섹션을 추가하는 것부터 시작하겠습니다:

  • Service 을 확장할 때 서비스 섹션을 일반 안드로이드 서비스로 선언하면 됩니다. <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 라이브러리가 로드되지 않으므로 통신을 위한 네이티브 호출을 사용할 수 없습니다. 별도의 프로세스에서 실행하려면 서비스 태그에 이를 추가하세요:

    android:process=":qt_service"
  • QtService 을 확장할 때는 Qt에 필요한 모든 필요한 라이브러리를 로드하기 위한 다른 항목을 선언해야 하는데, 주로 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() 함수에서 애플리케이션의 실행을 처리할 수 있습니다. 서비스 본문에 다음 인수 선언을 추가하세요:

<!-- 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 라이브러리 파일을 사용하는 경우 애플리케이션의 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";
   반환-1; }
별도의 .so 라이브러리 파일 사용

이 경우 서비스에 다른 실행 파일을 제공하는 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는 안드로이드 서비스와 통신하기 위한 다양한 프로세스 간 통신(IPC) 메서드를 제공합니다. 프로젝트의 구조에 따라 Java Service의 네이티브 C++ 호출이나 Android BroadcastReceiver를 사용할 수 있습니다.

Java Service의 네이티브 C++ 호출

QtActivity 와 동일한 프로세스에서 실행되는 서비스에서 작동할 수 있으며 Service 이 확장된 경우에도 사용할 수 있습니다.

자세한 내용은 Qt 안드로이드 알림 예제를 참조하세요.

안드로이드 브로드캐스트 수신기 사용하기

안드로이드 브로드캐스트 수신기는 안드로이드 시스템, 앱, 활동 및 서비스 간에 메시지를 교환할 수 있게 해줍니다. 다른 안드로이드 기능과 마찬가지로, 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);
            }
        }
    };
}

이 모든 것을 활용하려면 서비스 시작에 표시된 대로 서비스를 시작한 다음 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);
    }
};

그런 다음 메인 애플리케이션과 서비스 .pro 파일 모두에 .rep 파일을 추가합니다:

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와 안드로이드는 친구입니다!");

이 예제는 메인 애플리케이션의 프로세스에서 서비스로 메시지를 보냅니다. 서비스는 동일한 메시지로 응답하고 디버그 로그캣에 인쇄됩니다.

참고: 동일한 .so 라이브러리 파일을 사용할 때도 동일한 방법을 사용할 수 있습니다. 자세한 내용은 동일한 .so 라이브러리 파일 사용을 참조하세요.

QAndroidBinder 사용

QAndroidBinder안드로이드에서 가장 중요한 메서드인 바인더를 구현하여 프로세스 간 통신을 가능하게 하는 편의 클래스입니다. 이를 통해 프로세스 간에 QByteArray 또는 QVariant 객체를 전송할 수 있습니다.

참고: 안드로이드용 Qt는 하나의 프로세스에서 여러 서비스를 실행할 때 한 번에 하나의 서비스만 강제 실행하는 제한이 있습니다. 따라서 각 서비스를 자체 프로세스에서 실행하는 것이 좋습니다. 자세한 내용은 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.