Простая реализация

Вы когда-нибудь задумывались, как некоторые приложения, такие как Spotify, Google Maps и Google Meet, продолжают работать даже после того, как пользователь/система закрыли приложение? Ну, как вы уже догадались, это из-за служб переднего плана.

Мы создадим простое приложение секундомера, которое будет использовать сервис для включения всех функций секундомера. Как только приложение переходит в фоновый режим, мы преобразуем фоновую службу в службу переднего плана, чтобы наш секундомер продолжал работать, даже если приложение находится в фоновом режиме.

Приложение, которое мы будем создавать в действии:

Что, черт возьми, вообще такое служба переднего плана?

Итак, вот что говорится в официальной документации Android:

Службы переднего плана выполняют операции, заметные пользователю.

Службы переднего плана отображают уведомление в строке состояния, чтобы пользователи знали, что ваше приложение выполняет задачу на переднем плане и потребляет системные ресурсы. Уведомление нельзя закрыть, пока служба не будет остановлена ​​или удалена с переднего плана.

Я сделаю это немного яснее для вас.

Любая служба может выполнять фоновые задачи, и это нормально, пока ваше приложение видно пользователю, но как только приложение будет убито пользователем или бездействует в течение длительного времени, Android может решить убить процесс, и это приведет к ваша служба, в свою очередь, убита.

Чтобы решить эту проблему, мы используем службы переднего плана, которые гарантируют, что наша служба не будет уничтожена Android, когда приложение находится в фоновом режиме.

Из соображений безопасности служба переднего плана всегда должна отображать уведомление, которое нельзя закрыть до тех пор, пока задача не будет завершена, чтобы пользователь знал, что что-то происходит в фоновом режиме.

Пришло время создать приложение «Секундомер».

Да, мы закончили с теорией, теперь давайте создадим приложение.

Во-первых, давайте добавим разрешение, необходимое для использования служб переднего плана.

Добавьте следующую строку в свой AndroidManifest.xml

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

Теперь мы начнем создавать StopwatchService, который будет обрабатывать основную логику работы секундомера.

Создание класса обслуживания:

Теперь нам нужно сообщить системе Android, что у нас есть служба. Для этого мы добавим это в наш AndroidManifest.xml под тегом приложения:

Давайте создадим некоторые константы в StopwatchService.kt, которые помогут нам в общении со службой:

Класс обслуживания требует, чтобы мы переопределили две функции. Давайте сделаем это сейчас.

Начиная с Android 8.0 нам нужно создать канал уведомлений.

И пока мы этим занимаемся, мы также создадим функцию для получения доступа к NotificationManager системы для любого будущего использования.

Теперь давайте определим каждую функцию и настроим их функциональность.

Мы успешно реализовали все функции, которые позволили бы нам использовать этот сервис для запуска Секундомера, пока приложение живо.

Но теперь давайте реализуем функции, которые позволят нам создавать, обновлять уведомления и запускать/останавливать службу переднего плана.

Теперь давайте воспользуемся сервисом в MainActivity, чтобы сделать приложение функциональным

Определение двух широковещательных приемников. Один отвечает за получение статуса секундомера, а другой отвечает за получение прошедшего времени.

Приемник широковещательной рассылки (приемник) – это компонент Android, который позволяет вам регистрироваться для системных или прикладных событий. Все зарегистрированные получатели события получают уведомление от среды выполнения Android, как только это событие происходит.

В onResume мы инициализируем BroadcastReceivers следующим образом:

Функции updateLayout и updateStopwatchValueиспользуются для обновления макета соответствующими значениями, полученными от широковещательных приемников.

Компонент Android (служба, приемник, активность) может инициировать выполнение службы с помощью метода startService(intent).

Если вызывается метод startService(intent), а служба еще не запущена, создается объект службы и вызывается метод onCreate() службы.

После запуска службы вызывается метод onStartCommand(intent) в службе. Он передается в объект Intent из вызова startService(intent).

Давайте определим и добавим все функции, которые будут использоваться для связи с сервисом:

Запуск соответствующей функции при нажатии кнопки в onCreate:

Теперь для последней части нам нужно вызвать функции moveToForeground и moveToBackground из onPause и onStart соответственно, чтобы должным образом запустить и остановить службу переднего плана.

Репозиторий проекта



Несколько указателей

  • Если вы вызовете startService из фона, он выдаст IllegalStateException и вылетит
  • Вы можете использовать startForegroundService (или ContextCompat.startForegroundService), чтобы сообщить системе о характере вашей услуги. Если вы это сделаете, вам нужно вызвать метод startForeground из вашего класса обслуживания в течение 5 секунд.
  • Служба переднего плана не будет работать с идентификатором, равным 0 (я понятия не имел об этом, и мне пришлось потратить часы на отладку, почему моя служба переднего плана не работала)

Спасибо за чтение.