Запуск активной службы

Чтобы запустить активную службу из приложения, нужно выполнить два шага. Сначала запустите службу, вызвав 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 на случай, если он пытается запустить активную службу в недопустимой ситуации (например, если он пытается перевести службу в активный режим, пока приложение находится в фоновом режиме).