前台服务

前台服务用于执行用户可察觉的操作。

前台服务显示状态栏 通知,让用户知道您的 应用正在前台执行任务并消耗系统资源。

使用前台服务的应用示例包括:

  • 一款音乐播放器应用,用于在前台服务中播放音乐。通知 可能会显示当前正在播放的歌曲。
  • 一款健身应用,可在前台服务中记录用户的跑步数据。 从用户处获取权限通知可能会显示距离 用户在当前健身课程中走过的距离。

仅在应用需要执行任务时使用前台服务 用户会注意到这一点,即使用户并非直接与 应用。如果此操作的重要性较低,以至于您想使用 最低优先级通知,创建背景 任务

本文档介绍了使用前台服务所需的权限, 以及如何启动前台服务并将其从后台移除。它还 介绍了如何将某些用例与前台服务类型相关联,以及 启动前台服务时生效的访问限制 来自在后台运行的应用

用户可以默认关闭通知

从 Android 13(API 级别 33)开始,用户可以关闭通知 与前台服务相关联。为此,用户只需滑动一下手指 手势。传统上,通知 不会关闭,除非停止或移除前台服务 从前台运行

如果您希望用户不可关闭通知,请将以下代码传递 true进入setOngoing() 方法,然后使用 Notification.Builder 创建通知。

立即显示通知的服务

如果某项前台服务至少具有以下特征之一,则 系统会在服务启动后立即显示相关通知, (即使是在搭载 Android 12 或更高版本的设备上):

在 Android 13(API 级别 33)或更高版本中,如果用户拒绝 通知权限, 他们仍会在 任务管理器 但在抽屉式通知栏中看不到它们

在清单中声明前台服务

在应用清单中,声明应用的每个前台服务 (带有 <service>) 元素。对于每项服务,请使用 android:foregroundServiceType 属性 来声明服务执行的工作类型。

例如,如果您的应用创建了一个用于播放音乐的前台服务,则 可能会按如下方式声明 Service:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  <application ...>

    <service
        android:name=".MyMediaPlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="false">
    </service>
  </application>
</manifest>

如果您的服务有多个类型,请使用 | 将其分隔开 运算符。例如,使用摄像头和麦克风的服务 会进行如下声明:

android:foregroundServiceType="camera|microphone"

请求前台服务权限

以 Android 9(API 级别 28)或更高版本为目标平台并使用前台服务的应用 需要请求 FOREGROUND_SERVICE 应用清单中,如以下代码段所示。这属于正常情况 权限,因此系统会 并自动将其授予发出请求的应用。

此外,如果应用面向 API 级别 34 或更高级别,则它必须请求 前台服务将是哪种工作的适当权限类型 工作内容每种前台服务类型 都没有对应的权限类型例如,如果某个应用启动了一个 前台服务,则必须同时请求 FOREGROUND_SERVICEFOREGROUND_SERVICE_CAMERA 权限。这些都是普通权限,因此系统会授予它们 它们会自动列出

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

    <application ...>
        ...
    </application>
</manifest>

前台服务前提条件

从 Android 14(API 级别 34)开始,当您启动前台服务时, 系统会根据服务类型检查满足特定前提条件的情况。例如: 如果您尝试启动 location 类型的前台服务,系统会检查 确保您的应用已有 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 权限。否则,系统会抛出 SecurityException

因此,您必须确认满足必要的前提条件 启动前台服务前。前台服务 type 文档 列出了每种前台服务类型的所需前提条件。

启动前台服务

在请求系统将服务作为前台服务运行之前,请先启动 相应服务本身:

Kotlin

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

Java

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

在该 Service 内(通常在 onStartCommand() 中),您可以请求 确保服务在前台运行为此,请调用 ServiceCompat.startForeground() (在 androidx-core 1.12 及更高版本中提供)。此方法采用以下 参数:

这些类型可能是清单中声明的类型的子集, 具体取决于应用场景然后,如果您需要添加更多服务类型 您可以再次呼叫 startForeground()

例如,假设某个健身应用运行了一项跑步跟踪器服务,该服务始终 需要 location 信息,但不一定需要播放媒体。您 需要在清单中同时声明 locationmediaPlayback。如果 用户开始跑步并只想跟踪其位置,您的应用应调用 startForeground() 并仅传递 ACCESS_FINE_LOCATION 权限。然后, 如果用户想要开始播放音频,请再次调用 startForeground(),并 传递所有前台服务类型的按位组合(在本例中为 ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK)。

以下是启动相机前台服务的示例:

Kotlin

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

Java

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

从前台移除服务

要从前台移除服务,请调用 stopForeground()。 此方法采用布尔值,指示是否移除状态栏 通知。请注意,服务会继续运行。

如果您在服务在前台运行时将其停止,其通知 已移除。

处理用户发起的停止运行前台服务的应用的行为

抽屉式通知栏底部有一个按钮,指示
    当前在后台运行的应用的数量。按
    点击此按钮,系统会显示一个对话框,其中列出了不同应用的名称。通过
    每个应用右侧都有“停止”按钮
图 1. 符合以下条件的设备上的任务管理器工作流: 搭载 Android 13 或更高版本。

从 Android 13(API 级别 33)开始,用户可以从以下位置完成工作流: 抽屉式通知栏 停止具有持续前台服务的应用 目标 SDK 版本。这一功能称为 任务管理器,其中显示了 当前运行前台服务。

此列表的标签为使用中的应用。 每个应用旁边都有一个停止按钮。图 1 展示了 设备上运行的任务管理器工作流 Android 13。

当用户在界面中按应用旁边的 Stop 按钮时, 任务管理器,则会发生以下操作:

  • 系统会从内存中移除您的应用。因此,您的整个应用都会停止, 而不仅仅是正在运行的前台服务
  • 系统会移除应用的 activity 返回堆栈。
  • 任何媒体播放都会停止。
  • 与前台服务相关联的通知会被移除。
  • 您的应用仍会保留在历史记录中。
  • 已安排的作业会在计划的时间执行。
  • 闹钟会在预定的时间或时间范围响起。

要测试您的应用在用户停止运行时和之后是否按预期运行, 应用中,请在终端窗口中运行以下 ADB 命令:

adb shell cmd activity stop-app PACKAGE_NAME

豁免

系统会针对特定类型的应用提供几种级别的豁免,下面几部分将介绍这些豁免。

豁免针对的是应用而不是进程。如果系统豁免了应用中的一个进程,则该应用中的所有其他进程也会被豁免。

完全不会显示在任务管理器中

以下应用可以运行前台服务,并且不会显示在 任务管理器:

免于被用户停止

当以下类型的应用运行前台服务时,它们会显示在 但任务管理器旁边没有停止按钮, 供用户点按的应用名称:

使用专门构建的 API 而不是前台服务

对于很多应用场景,您可以使用平台 API 或 Jetpack API 完成工作 否则您可能需要使用前台服务如果有合适的 专用 API,因此您几乎应始终使用它,而不是使用前台 服务。专门构建的 API 通常提供其他特定于用例的 API 您必须自行构建这些功能。例如: Bubbles API 可处理 需要实现聊天气泡功能的即时通讯应用。

前台服务类型的相关文档列出了相关情况 用来替代前台服务的理想替代方案。

与从后台启动前台服务相关的限制

以 Android 12 或更高版本为目标平台的应用无法启动前台 除了一些特殊的 API 之外, 支持请求。如果应用尝试启动 前台服务,而应用在后台运行时; 无法满足任何特殊情况,则系统会抛出 ForegroundServiceStartNotAllowedException

此外,如果应用要启动一项前台服务 使用时的权限(例如身体传感器、摄像头、麦克风或 地点 权限),则无法在应用在后台运行时创建相应服务; 即使应用属于后台启动豁免的某一类 限制。对 启动在使用时需要的前台服务 权限

不受后台启动限制的约束

在以下情况下,您的应用可以启动前台服务,即使 您的应用将在后台运行:

对启动需要在使用时需要权限的前台服务实施了限制

在 Android 14(API 级别 34)或更高版本中,需要注意一些特殊情况 如果您要启动的前台服务需要在使用时获得权限,则会显示此图标。

如果您的应用以 Android 14 或更高版本为目标平台,那么操作系统 请在创建前台服务时进行检查,以确保应用具备 相应服务类型的适当权限例如,当您创建 前台服务类型 麦克风、 系统会验证您的应用目前 RECORD_AUDIO 权限。如果您没有该权限,系统会抛出 SecurityException

对于仅在使用时授予的权限,这可能会导致问题。如果您的应用具有 但它只有在位于 前台。这意味着,如果您的应用在后台运行,并且尝试创建 摄像头、位置信息或麦克风类型的前台服务,系统看到 您的应用目前不具备所需的权限,并引发 SecurityException

同样,如果您的应用在后台运行,它会创建一个 需要 BODY_SENSORS_BACKGROUND 权限的健康服务、应用 当前没有此权限,系统会抛出异常。 (如果它是一项需要不同权限的健康服务, 例如 ACTIVITY_RECOGNITION)。 PermissionChecker.checkSelfPermission() 不会避免此问题。如果您的应用具有仅在使用时授予的权限,并且 它会调用 checkSelfPermission() 以检查自己是否具有该权限,该方法 返回 PERMISSION_GRANTED,即使应用在后台运行也是如此。当 方法会返回 PERMISSION_GRANTED,这表示“您的应用具有此权限 应用使用期间。”

因此,如果您的前台服务需要“在使用中”权限, 在发生以下情况时,必须调用 Context.startForegroundService()Context.bindService(): 除非该服务属于 定义的豁免

不受使用时权限限制的约束

在某些情况下,即使应用启动了前台服务, 在后台,它仍然可以获取位置信息、 应用在前台运行时获取摄像头和麦克风信息 (“使用时”)。

在这些情况下,如果该服务声明了 前台服务类型 location,由符合以下条件的应用启动: 包含 ACCESS_BACKGROUND_LOCATION 这项服务可以随时访问位置信息,即使 应用在后台运行时

以下列表包含这些情况:

  • 系统组件启动服务。
  • 该服务首先与应用交互 微件
  • 服务首先与通知交互。
  • 该服务以 PendingIntent(通过 一个不同的可见应用
  • 该服务由符合设备规范的应用启动 控制器
  • 该服务由提供 VoiceInteractionService 的应用启动。
  • 该服务由具有 START_ACTIVITIES_FROM_BACKGROUND 特许权限。
确定应用中的哪些服务受到影响

测试您的应用时,请启动其前台服务。如果启动的服务对位置信息、麦克风和摄像头的访问受到限制,Logcat 中就会显示以下消息:

Foreground service started from background can not have \
location/camera/microphone access: service SERVICE_NAME