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

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

Android подталкивает вас к использованию связанных служб, таких как JobSchedulers, для выполнения задач в фоновом режиме, чтобы они были более эффективными и приводили к повышению производительности. Вам определенно следует подумать об использовании существующих связанных сервисов, прежде чем пытаться создать свои собственные. Бывают случаи, когда JobScheduler вам не поможет, например, навигация по карте или воспроизведение музыки. В этих случаях вам следует использовать службу переднего плана, которая также является запущенной службой, но с пользовательским интерфейсом (через уведомление). Это гарантирует, что Android его не убьет (или, по крайней мере, пока не случится дерьмо).

Создание сервиса

Все службы требуют, чтобы вы реализовали метод onBind. Поскольку это не привязанная услуга, мы просто вернем null

class RandomNumberGeneratorService:Service() { 
    override fun onBind(p0: Intent?): IBinder? { 
        return null 
    } 
}

Запуск службы

Чтобы запустить службу, нам нужно использовать явное намерение.

val startServiceIntent = Intent(this,RandomNumberGeneratorService::class.java)

startService(startServiceIntent)

На устройствах Android 8.0 и более поздних версий вызов startService из фона (например, из широковещательного приемника) приведет к исключению IllegalStateException. В этом случае используйте startForegroundService и вызовите startForeground внутри службы в течение 5 секунд, чтобы запустить службу переднего плана, пока ваше приложение работает в фоновом режиме.

Так как в Android 8.0 и выше неавторизованные сервисы могут быть убиты, мы сделаем наш сервис сервисом переднего плана. Для этого мы воспользуемся методом startForeground внутри службы. Этот метод принимает два аргумента: идентификатор уведомления (константу) и уведомление.

В Android 9.0 приложения, желающие использовать службы переднего плана, теперь должны сначала запрашивать разрешение FOREGROUND_SERVICE. Это обычное разрешение, поэтому система автоматически предоставляет его запрашивающему приложению. Запуск службы переднего плана без разрешения вызывает исключение SecurityException.

Чтобы остановить службу переднего плана изнутри службы, нам нужно вызвать два метода:

  • stopForeground. Этот метод принимает логическое значение, независимо от того, удалять ли уведомление или нет. Передача true удалит уведомление.
  • stopSelf - это остановит службу и поможет системе восстановить ресурсы. Существует также перегруженная версия этого метода, которая принимает startId. Перегруженная версия полезна, если служба одновременно обрабатывает несколько запросов к методу onStartCommand. Остановка службы приведет к прекращению других запросов. Итак, чтобы выполнить весь запрос, вы должны использовать stopSelf(int). Таким образом, если вы останавливаете службу для startId, в то время как поступил другой запрос с новым startId, служба не остановится, поскольку startId не совпадает.

Чрезмерно упрощенный способ остановки службы:

  • stopSelf () - всегда останавливает текущую службу.
  • stopSelf (startId) - останавливает текущую службу, но только если startId совпадает с идентификатором, указанным при последнем запуске службы.
  • stopService (Intent) - используется для остановки службы извне.

Если вы чувствуете, что вам не нужно придерживаться приоритета переднего плана, вы можете использовать метод stopForeground для удаления из состояния переднего плана, позволяя системе убить его, если это необходимо. Обратите внимание, что это не останавливает службу, а просто удаляет ее состояние переднего плана.

Поведение при перезапуске службы

onStartCommand вызывается каждый раз, когда служба запускается с помощью startService вызова, поэтому она будет вызываться несколько раз в течение жизненного цикла службы. В этом методе вы должны указать, как служба должна реагировать при перезапуске или когда система убивает ее перед завершением. Ниже перечислены несколько флагов:

  • START_STICKY - это стандартное поведение. В этом случае onStartCommand будет вызываться каждый раз при перезапуске службы после того, как она была убита системой. Обратите внимание, что в этом случае переданное намерение будет null, когда система перезапустит службу. Этот флаг используется, если либо служба сама управляет своим жизненным циклом, либо управляется другими компонентами с использованием startService и stopService.
  • START_NOT_STICKY - в этом режиме служба будет перезапущена только при наличии ожидающих вызовов startService.
  • START_REDELIVER_INTENT - в этом режиме служба будет перезапущена, если есть незавершенные вызовы для запуска, или если система завершит работу службы до того, как служба вызовет stopSelf. В этом случае последнее переданное намерение будет доставлено повторно.

Внутри onStartCommand вы можете определить, какой режим перезапуска вызвал запуск этого onStartCommand. Вы можете узнать, какое из двух истинно:

  • START_FLAG_REDELIVERY - этот флаг означает, что намерение доставляется повторно, поскольку служба уничтожила службу до того, как она смогла вызвать stopSelf.
  • START_FLAG_RETRY - указывает, что Служба была перезапущена после аварийного завершения. Он передается, когда для Службы ранее было установлено значение START_STICKY.

Я создал бесплатное приложение для Android, используя указанный выше компонент. Приложение помогает отслеживать свои привычки, используя уникальный способ вознаграждения. Не стесняйтесь проверить это на http://bit.ly/uprewired :)

Первоначально опубликовано на www.aanandshekharroy.com.

Спасибо, что прочитали.

Вот еще несколько статей, которые могут вас заинтересовать: