本页内容

Android 服务

您可以使用 Qt 创建 Android 服务。服务是一种在后台运行的组件,因此没有用户界面。它适用于执行长期操作,例如记录 GPS 数据、等待社交媒体通知等。即使启动该服务的应用程序已退出,服务仍会继续运行。

构建服务

首先,请按照《使用 Android 功能扩展 Qt》中的说明创建一个 Android 包目录。该目录中包含一个名为 `AndroidManifest.xml ` 的文件。在包目录内,创建一个名为 `src ` 的目录,您所有的 Java 包和类都将创建在此目录中。

创建服务类

在决定使用QtService 还是Android Service时,其考量逻辑与Activity的情况相同。除非您需要使用必须加载Qt库的功能(例如Qt的原生调用和事件处理),否则继承Service 通常是可行的。

你可以通过让 Java 类继承QtService 或Service类来创建服务。根据你是否希望在服务中使用 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 时,这两种方式均可实现。

按需启动服务

您可以通过以下方式启动服务:

  • 直接在 C++ 中使用 `QAndroidIntent ` 和 `QJniObject`,通过创建服务Intent并调用应用的主活动方法`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 所需的所有必要库,主要与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);
    returnapp.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>

intmain(intargc, char *argv[])
{
    qWarning() << "Service starting from a separate .so file";
    QAndroidService app(argc,argv);

    returnapp.exec();
}

在AndroidManifest.xml 中为该服务定义android.app.lib_name :

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

与服务的通信

Qt for Android 提供了多种进程间通信(IPC)方法,用于与 Android 服务进行通信。根据项目的结构,您可以选择通过 Java 服务或 Android BroadcastReceiver 进行原生 C++ 调用。

从 Java 服务发起原生 C++ 调用

此方法适用于与QtActivity 在同一进程中运行的服务,即使该服务继承了Service 类也不受影响。

有关更多信息,请参阅Qt for Android 通知器示例。

使用 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 的主 Activity 中创建并注册广播接收器。最简单的方法是创建一个带有方法的自定义类,并在 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);
            }
        }
    };
}

要使用这些功能,请按照“启动服务”中的说明启动服务,然后通过调用方法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>

intmain(intargc, char *argv[])
{
    qWarning() << "QtAndroidService starting from separate .so";
    QAndroidService app(argc,argv);

    QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica")));
    ServiceMessenger serviceMessenger;
    srcNode.enableRemoting(&serviceMessenger);

    returnapp.exec();
}

然后,在应用程序的main() 函数中,连接到源节点:

QRemoteObjectNode repNode;
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&message){
    qDebug() << "Service sent: " << message;
});
rep->ping("Qt 和 Android 是好朋友!");

此示例从主应用程序的进程向服务发送一条消息。服务会回复相同的内容,该内容将显示在调试日志(Logcat)中。

注意: 在使用相同的.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.