Липкий запуск сервисов в Android-e.


В Android API есть такой абстрактный класс как Service. Он является наследником ContextWrapper-а, который в свою очередь является наследником Context-a. При некоторых допущениях можно относиться к сервисам как к “активити без UI” (хотя это не совсем правильно в деталях). Использовать сервис рекомендуется для задач не требующих прямого вмешательства пользователя.

В документации особо акцентируется внимание на том, что Service не является ни процессом, ни ниткой. Если сервис должен делать какую-то “тяжелую” работу, то нужно самому выносить ее в отдельный thread, чтобы не получить ANR (Application Not Responding).

Если делать свой thread лениво, то можно использовать готовый класс для асинхронной работы IntentService.

Про него нужно знать следующее:

  • IntentService наследуется от Service.
  • Является абстрактным классом.
  • Нужно реализовать в нём метод onHandleIntent(Intent).
  • Обработка intent-ов идёт поочередно, но не в главном, а в своем выделенном thread-e.

Диаграмма классов сервисов выглядит следующим образом:

В работе с сервисами много всего интересного.
Здесь я хотел более подробно остановится только на запуске сервиса, а именно каким образом задается поведение сервиса если он внезапно завершился не по своей воли (например по причине нехватки памяти). Есть три основные “стратегии” поведения в таких ситуациях.
Каждой “стратегии” соответствует константа, которую нужно вернуть сервису при отработки запроса на запуск (метод onStartCommand). Есть еще устаревшая константа, оставлена для совместимости, мы ее трогать не будем.

Другими словами:

class MyService extends Service {
    // ....
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // что делать если внезапно сервис грохнут?
        return START_STICKY;
    }
}

Описание констант

Рассмотрим что именно определяют эти константы.

START_STIСKY

Если вдруг случилось плохое (например не хватило памяти) и наш сервис убили, то потом (например когда памяти будет достаточно) андроид сам будет пытаться запустить сервис еще раз. Это значит что будет создан новый объект Service, у которого потом будет вызван метод onStartCommand.
Важно то, что при таком “оживлении” сервиса, в метод onStartCommand будет передавать null вместо объекта Intent-a.
Нужно к этому быть готовым.

START_REDELIVER_INTENT

Если сервис умер не своей смертью как и в предыдущем случае, то мы тоже будем пытаться его оживить. Теперь при “оживлении” мы будем подкладывать ему в onStartCommand тот Intent, который он не смог отработать когда был живой. Таким образом мы говорим, что наш сервис – это очень упорный сервис и мы хотим отработать полученный intent до конца!

START_NOT_STIСKY

Ну сдох сервис, так сдох.

Вроде бы все просто, но многие android-программисты путаются в этих “STICKY”, “NOT_STICKY” и т.д.. В итоге просто всегда указывают “START_STICKY” (на всякий случай, т.к. так написано в примере к API). Это конечно надежно, но не совсем правильно. Чтобы стало совсем всё понятно, я расписал такую блок-схему.

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

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

Телеграм канал: @prgrmdr
Почта для связи: vit [at] programmisty.com