Запуск службы переднего плана

Для запуска службы переднего плана из вашего приложения необходимо выполнить два шага. Во-первых, необходимо запустить службу, вызвав метод context.startForegroundService() . Затем служба должна вызвать метод ServiceCompat.startForeground() , чтобы перейти в режим службы переднего плана.

Предварительные требования

В зависимости от того, на какой уровень API ориентировано ваше приложение, существуют некоторые ограничения на то, когда приложение может запускать службу переднего плана.

  • Приложениям, ориентированным на Android 12 (уровень API 31) или выше, запрещено запускать фоновые службы, пока приложение находится в фоновом режиме, за некоторыми исключениями. Для получения дополнительной информации, а также информации об исключениях из этого правила, см. раздел «Ограничения на запуск фоновых служб из фонового режима» .

  • Приложения, ориентированные на Android 14 (уровень API 34) или выше, должны запрашивать соответствующие разрешения для типа службы переднего плана. Когда приложение пытается перевести службу на передний план, система проверяет наличие необходимых разрешений и генерирует исключение SecurityException если какие-либо разрешения отсутствуют. Например, если вы пытаетесь запустить службу переднего плана типа location , система проверяет, есть ли у вашего приложения уже разрешение ACCESS_COARSE_LOCATION или ACCESS_FINE_LOCATION . В документации по типам служб переднего плана перечислены необходимые предварительные условия для каждого типа службы переднего плана.

Запуск сервиса

Для запуска службы переднего плана необходимо сначала запустить её как обычную (не переднюю) службу:

Котлин

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 ).

В следующем примере показан код, который служба камеры использовала бы для преобразования себя в службу переднего плана:

Котлин

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