从应用启动前台服务需要两个步骤。首先,您必须通过调用 context.startForegroundService()
来启动服务。然后,让服务调用 ServiceCompat.startForeground()
以将其提升为前台服务。
前提条件
根据您的应用所面向的 API 级别,应用何时可以启动前台服务存在一些限制。
以 Android 12(API 级别 31)或更高版本为目标平台的应用,不允许在应用处于后台时启动前台服务,但有少数特定例外情况。如需了解更多信息以及此规则的例外情况,请参阅从后台启动前台服务的限制。
以 Android 14(API 级别 34)或更高版本为目标平台的应用必须请求相应的前台服务类型权限。当应用尝试将服务提升为前台服务时,系统会检查相应的权限,如果应用缺少任何权限,则会抛出
SecurityException
。例如,如果您尝试启动location
类型的前台服务,系统会检查以确保您的应用已拥有ACCESS_COARSE_LOCATION
或ACCESS_FINE_LOCATION
权限。前台服务类型文档列出了每种前台服务类型所需的先决条件。
启动服务
要启动前台服务,您必须首先将其作为普通(非前台)服务启动
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);
关于代码的要点
- 此代码段会启动一个服务。但是,该服务尚未在前台运行。在服务内部,您需要调用
ServiceCompat.startForeground()
以将服务提升为前台服务。
将服务提升到前台
服务运行后,您需要调用 ServiceCompat.startForeground()
以请求服务在前台运行。通常,您会在服务的 onStartCommand()
方法中调用此方法。
ServiceCompat.startForeground()
接受以下参数
- 服务。
- 一个正整数,用于在状态栏中唯一标识服务的通知。
- Notification 对象本身。
- 标识服务所执行工作的前台服务类型(可包含多种类型)
您传递给 startForeground()
的前台服务类型应与清单中声明的类型一致,具体取决于您的使用场景。然后,如果您需要添加更多服务类型,可以再次调用 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) } // ... } } //... }
关于代码的要点
- 应用已在清单中声明它需要
CAMERA
权限。但是,应用还必须在运行时检查以确保用户已授予该权限。如果应用实际上没有正确的权限,则应告知用户此问题。 - 不同的前台服务类型随不同版本的 Android 平台引入。此代码会检查其运行的 Android 版本,并请求相应的权限。
- 此代码会检查
ForegroundServiceStartNotAllowedException
,以防其尝试在不允许的情况下启动前台服务(例如,如果它尝试在应用处于后台时将服务提升为前台服务)。