安卓服务

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

组装服务

要开始操作,请按照Qt Creator 中的说明创建 Android 软件包目录: Android 部署配置。该目录包含AndroidManifest.xml 文件。在软件包目录下创建src 目录,在此创建所有 Java 软件包和类。

创建服务类

您可以通过扩展QtServiceAndroid.Service 类来创建服务:Service扩展到您的 Java 类,从而创建服务。根据您是想在服务中使用 Qt XML 功能还是从 Java 中调用本地 C++ 函数,您需要扩展QtServiceService 。让我们从一个简单的服务开始,如下所示:

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++ 使用QAndroidIntentQJniObject ,创建服务Intent并调用应用程序的主活动方法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 类可能不起作用。更多信息,请参阅 Android 建议使用前景服务JobIntentService

在 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 库。要在独立进程中运行,请在服务标签中添加以下内容:

    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 库文件

使用与主活动相同的.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() 函数会被执行两次,一次是启动主活动,第二次是启动服务。因此,您必须根据所提供的参数处理每次执行。一种方法如下:

if(argc<= 1) {// 处理主活动执行的代码}else 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 模板的子项目,为服务提供不同的可执行文件。.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>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 提供了多种进程间通信(IPC)方法来与 Android 服务通信。根据项目的结构,您可以使用 Java Service 或 Android BroadcastReceiver 的本地 C++ 调用。

从 Java 服务调用本地 C++

这种方法适用于与QtActivity 运行在同一进程中的服务,即使Service 已经扩展。

更多信息,请参阅Qt for Android Notifier 示例

使用 Android 广播接收器

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;
}

然后,您需要从 Active 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 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>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&message){    qDebug() << "Service sent: " << message;
}); rep->ping("Qt 和 Android 是朋友!");

此示例从主应用程序进程向服务发送一条消息。服务回复同样的消息,并打印在调试日志猫上。

注: 使用相同的.so lib 文件时,也可使用相同的方法。更多信息,请参阅使用相同的 .so 库文件

使用 QAndroidBinder

QAndroidBinder 是一个便利类,它通过实现Android 中最重要的方法来实现进程间通信:Binder。它允许在进程间发送 或 对象。QByteArray QVariant

注意: Qt for Android 有一个限制,即在一个进程中运行多个服务时,一次只能执行一个服务。因此,建议在独立进程中运行每个服务。有关详细信息,请参阅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.