前台服务执行可被用户注意到的操作。
前台服务需要显示状态栏通知,让用户知道您的应用正在前台执行任务并消耗系统资源。
使用前台服务的应用示例包括:
- 一款音乐播放器应用,用于在前台服务中播放音乐。通知可能会显示当前正在播放的歌曲。
- 一款健身应用,在获得用户许可后,可在前台服务中记录用户跑步数据。通知可能会显示用户在当前健身活动期间行进的距离。
仅当应用需要执行可被用户注意到的任务时(即使该任务未直接与应用交互),才使用前台服务。如果操作不是特别重要,因而您希望使用最低优先级通知,请改为创建后台任务。
本文档介绍了使用前台服务所需的权限,以及如何启动前台服务并将其从后台移除。其中还介绍了如何将某些用例与前台服务类型相关联,以及从在后台运行的应用启动前台服务时生效的访问权限限制。
默认情况下,用户可以关闭通知
从 Android 13(API 级别 33)开始,用户默认可以关闭与前台服务相关联的通知。为此,用户需要对通知执行滑动手势。传统上,除非前台服务停止或从前台移除,否则系统不会关闭通知。
如果您希望用户无法关闭通知,请在使用 Notification.Builder
创建通知时将 true
传递给 setOngoing()
方法。
立即显示通知的服务
如果某项前台服务至少具有以下特征之一,则系统会在服务启动后立即显示相关通知,即使在搭载 Android 12 或更高版本的设备上也是如此:
- 该服务与包含操作按钮的通知相关联。
- 该服务的
foregroundServiceType
为mediaPlayback
、mediaProjection
或phoneCall
。 - 该服务根据通知的类别属性中的定义,提供与通话、导航或媒体播放相关的用例。
- 该服务通过在设置通知时将
FOREGROUND_SERVICE_IMMEDIATE
传入setForegroundServiceBehavior()
,已停用行为变更。
在 Android 13(API 级别 33)或更高版本中,如果用户拒绝授予通知权限,他们仍会在任务管理器中看到与前台服务相关的通知,但不会在抽屉式通知栏中看到这些通知。
在清单中声明前台服务
在应用的清单中,使用 <service>
元素声明应用的每项前台服务。对于每项服务,请使用 android:foregroundServiceType
属性声明该服务执行的工作类型。
例如,如果您的应用创建了用于播放音乐的前台服务,您可以按如下方式声明该服务:
<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_SERVICE
和 FOREGROUND_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_LOCATION
或 ACCESS_FINE_LOCATION
权限。如果没有,系统会抛出 SecurityException
。
因此,您必须先确认已满足所需的前提条件,然后再启动前台服务。前台服务类型文档列出了每种前台服务类型的必需前提条件。
启动前台服务
在请求系统将服务作为前台服务运行之前,请先启动服务本身:
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);
在服务内(通常在 onStartCommand()
中),您可以请求让服务在前台运行。为此,请调用 ServiceCompat.startForeground()
(适用于 androidx-core 1.12 及更高版本)。此方法采用以下参数:
- 服务
- 一个正整数,用于唯一标识状态栏中的通知
Notification
对象本身- 用于标识服务所执行工作的前台服务类型
这些类型可能是清单中声明的类型的一部分,具体取决于具体用例。然后,如果您需要添加更多服务类型,可以再次调用 startForeground()
。
例如,假设一款健身应用运行一项始终需要 location
信息但可能不需要播放媒体的跑步跟踪器服务。您需要在清单中同时声明 location
和 mediaPlayback
。如果用户开始跑步,并且只希望跟踪其位置信息,您的应用应调用 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()
。此方法采用布尔值,指示是否需同时移除状态栏通知。请注意,该服务会继续运行。
如果您在服务在前台运行时将其停止,则其通知会被移除。
处理用户发起的停止运行前台服务的应用
从 Android 13(API 级别 33)开始,无论应用采用何种目标 SDK 版本,用户都可以通过抽屉式通知栏完成工作流,以停止具有持续前台服务的应用。此功能称为任务管理器,会显示当前正在运行前台服务的应用列表。
此列表的标签为使用中的应用。每个应用旁边都有一个停止按钮。图 1 说明了搭载 Android 13 的设备上的任务管理器工作流程。
当用户在任务管理器中按您应用旁边的停止按钮时,系统会执行以下操作:
- 系统会将您的应用从内存中移除。因此,系统会停止您的整个应用,而不仅仅是正在运行的前台服务。
- 系统会移除应用的 activity 返回堆栈。
- 所有媒体播放都会停止。
- 系统会移除与前台服务关联的通知。
- 您的应用仍会保留在历史记录中。
- 预定作业会在预定的时间执行。
- 闹钟会在设定的时间或时间段响铃。
如需测试应用在用户停止应用的过程中和之后的行为是否符合预期,请在终端窗口中运行以下 adb 命令:
adb shell cmd activity stop-app PACKAGE_NAME
豁免
系统会针对特定类型的应用提供几种级别的豁免,下面几部分将介绍这些豁免。
豁免针对的是应用而不是进程。如果系统豁免了应用中的一个进程,则该应用中的所有其他进程也会被豁免。
完全不会显示在任务管理器中
以下应用可以运行前台服务,而完全不会显示在任务管理器中:
- 系统级应用
- 安全应用,即具有
ROLE_EMERGENCY
角色的应用 - 处于演示模式的设备上的应用
免于被用户停止
当以下类型的应用运行前台服务时,它们会显示在任务管理器中,但应用名称旁边没有可以供用户点按的停止按钮:
- 设备所有者应用
- 资料所有者应用
- 常驻应用
- 具有
ROLE_DIALER
角色的应用
使用专为特定用途构建的 API 代替前台服务
对于许多用例,您可以使用平台 API 或 Jetpack API 来执行您原本可能使用前台服务执行的工作。如果有适合的专用 API,您几乎总是应使用该 API,而不是使用前台服务。专用 API 通常提供其他特定于用例的功能,否则您将不得不自行构建这些功能。例如, Bubbles API 可为需要实现聊天气泡功能的消息应用处理复杂的界面逻辑。
前台服务类型文档列出了可替代前台服务的良好替代方案。
与从后台启动前台服务相关的限制
以 Android 12 或更高版本为目标平台的应用无法在后台运行时启动前台服务,但少数特殊情况除外。如果应用在后台运行时尝试启动前台服务,并且前台服务不符合任何特殊情况,则系统会抛出 ForegroundServiceStartNotAllowedException
。
此外,如果应用想要启动需要使用时权限(例如身体传感器、摄像头、麦克风或位置信息权限)的前台服务,则无法在应用处于后台时创建该服务,即使应用属于后台启动限制的某项例外情况也不例外。原因请参阅对需要使用中权限的前台服务启动施加的限制部分。
不受后台启动限制
在以下情况下,即使您的应用在后台运行,也可以启动前台服务:
- 您的应用从用户可见的某种状态(如 activity)过渡。
- 您的应用可以从后台启动 activity,但该应用在现有任务的返回堆栈中具有 activity 的情况除外。
您的应用使用 Firebase Cloud Messaging 接收高优先级消息。
您的应用调用精确闹钟来完成用户请求的操作。
您的应用是设备的当前输入法。
设备重新启动并在广播接收器中接收
ACTION_BOOT_COMPLETED
、ACTION_LOCKED_BOOT_COMPLETED
或ACTION_MY_PACKAGE_REPLACED
intent 操作之后。您的应用在广播接收器中接收
ACTION_TIMEZONE_CHANGED
、ACTION_TIME_CHANGED
或ACTION_LOCALE_CHANGED
intent 操作。您的应用从
NfcService
收到ACTION_TRANSACTION_DETECTED
事件。您的应用使用配套设备管理器,并声明
REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
权限或REQUEST_COMPANION_RUN_IN_BACKGROUND
权限。请尽可能使用REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
。用户为您的应用关闭了电池优化。
您的应用拥有
SYSTEM_ALERT_WINDOW
权限。注意:如果您的应用以 Android 15 或更高版本为目标平台,则必须具有SYSTEM_ALERT_WINDOW
权限,并且应用当前必须有一个可见的叠加窗口。
对需要在使用时权限的前台服务启动施加的限制
在 Android 14(API 级别 34)或更高版本中,如果您要启动需要“使用时”权限的前台服务,则需要注意一些特殊情况。
如果您的应用以 Android 14 或更高版本为目标平台,操作系统会在您创建前台服务时进行检查,以确保您的应用拥有该服务类型的所有适当权限。例如,当您创建类型为麦克风的前台服务时,操作系统会验证您的应用当前是否具有 RECORD_AUDIO
权限。如果您没有此权限,系统会抛出 SecurityException
。
对于“使用时”权限,这可能会导致潜在问题。如果您的应用具有“在使用时”权限,则只有在前台运行时才拥有该权限。这意味着,如果您的应用在后台运行,并尝试创建类型为相机、位置信息或麦克风的前台服务,系统会发现您的应用目前没有所需的权限,并抛出 SecurityException
。
同样,如果您的应用在后台运行,并且创建了需要 BODY_SENSORS
权限的健康服务,那么该应用目前没有该权限,并且系统会抛出异常。(如果是需要其他权限(例如 ACTIVITY_RECOGNITION
)的健康服务,则不适用。)调用 PermissionChecker.checkSelfPermission()
不会防止此问题。如果您的应用具有“在使用时”权限,并且调用 checkSelfPermission()
来检查自己是否具有该权限,即使应用在后台运行,该方法也会返回 PERMISSION_GRANTED
。当该方法返回 PERMISSION_GRANTED
时,表示“您的应用在使用期间拥有此权限”。
因此,如果您的前台服务需要“在使用时”权限,您必须在应用具有可见 activity 时调用 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