启动前台服务

如需从应用启动前台服务,您需要执行两个步骤。首先,您 必须通过调用 context.startForegroundService()启动服务。然后,让 服务调用 ServiceCompat.startForeground() 将自身提升为前台服务。

前提条件

根据应用的目标 API 级别,应用在启动前台服务时会受到一些限制。

  • 以 Android 12(API 级别 31)或更高版本为目标平台的应用不得在应用处于后台时启动前台服务,但有一些特定例外情况。如需了解详情以及有关此规则的 例外情况的信息,请参阅与从后台启动前台服务相关的限制 。

  • 以 Android 14(API 级别 34)或更高版本为目标平台的应用必须为前台服务类型请求适当的权限。当应用尝试将服务提升到前台时,系统会检查适当的权限,如果应用缺少任何权限,则会抛出 SecurityException。例如,如果您尝试启动类型为 location 的前台服务,系统会检查以确保您的应用已拥有 ACCESS_COARSE_LOCATIONACCESS_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() 采用以下参数:

您传递给 startForeground() startForeground() 的前台服务类型取决于清单中声明的类型,具体取决于具体 用例。然后,如果您需要添加更多服务类型,可以再次调用 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)
            }
            // ...
        }
    }

    //...
}

代码要点

  • 应用已在清单中声明需要 CAMERA 权限。不过,应用还必须在运行时进行检查,以确保用户授予了该权限。如果应用实际上没有正确的权限,则应让用户了解该问题。
  • 不同的前台服务类型是在不同版本的 Android 平台中引入的。此代码会检查它运行的 Android 版本,并请求适当的权限。
  • 该代码会检查 ForegroundServiceStartNotAllowedException,以防它尝试在不允许的情况下启动前台服务(例如,如果它尝试在应用处于后台时将服务提升到前台 while the app is in the background)。