Android foreground service что это

Полный список

— изучаем IntentService
— включаем режим Foreground для сервиса
— помещаем сервис в автозагрузку

Строили мы, строили, и, наконец, построили. Урок номер 100, с чем всех нас и поздравляю )

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

IntentService

Это подкласс обычного Service. Он используется, если вам в сервисе надо выполнять какие-то тяжелые задачи, и вы не хотите сами возиться с асинхронностью. Принцип работы этого вида сервиса прост. Он создает новый поток для своей работы. Затем берет все Intent пришедшие ему в onStartCommand и отправляет их на обработку в этот поток. Как именно обрабатываются Intent – зависит от нас, т.к. мы сами кодим это в методе onHandleIntent.

Т.е. приложение сыпет в сервис вызовами startService, в которых передает Intent-ы. IntentService принимает эти вызовы в onStartCommand, берет Intent-ы и отправляет их в очередь на обработку. И далее они поочередно обрабатываются в отдельном потоке методом onHandleIntent. Когда последний Intent из очереди обработан, сервис сам завершает свою работу.

В приложении делаем три вызова:

Где time – это время паузы, которую будем делать в сервисе, а label – просто метка, чтобы отличать вызовы.

Здесь необходим конструктор, в котором вызываем конструктор супер-класса и указываем какое-нить имя. Оно будет использовано для наименования потока.

В методе onHandleIntent кодим обработку Intent-ов. Достаем из них time и label, запускаем паузу на time секунд и выводим в лог label в начале и в конце.

В итоге, при запуске в логах видим:

11:07:37.880: D/myLogs(4137): onCreate
11:07:37.880: D/myLogs(4137): onHandleIntent start Call 1
11:07:40.880: D/myLogs(4137): onHandleIntent end Call 1
11:07:40.880: D/myLogs(4137): onHandleIntent start Call 2
11:07:41.880: D/myLogs(4137): onHandleIntent end Call 2
11:07:41.880: D/myLogs(4137): onHandleIntent start Call 3
11:07:45.890: D/myLogs(4137): onHandleIntent end Call 3
11:07:45.890: D/myLogs(4137): onDestroy

Сервис создался, вызовы выполнились по очереди и сервис завершил работу. От нас понадобилось только накодить обработку.

Foreground

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

На вход он принимает те же параметры, что и NotificationManager.notify – ID и Notification.

Т.е. вы создаете уведомление, назначаете ему ID и передаете это в startForeground. Сервис переходит в режим IDDQD :), а в статус-баре появилось уведомление.

Оно появилось в разделе для постоянных уведомлений (Ongoing).

Метод stopForeground (boolean removeNotification) — возвращает сервису способность быть убитым системой в случае острой нехватки памяти. А на вход он принимает boolean-значение – удалять уведомление из статус-бара или нет.

Уведомление также пропадет, когда сервис будет остановлен.

Эти методы работают, начиная с Android 2.0. Пример реализации для более ранних версий есть в хелпе.

Напомню, что уведомления мы научились создавать на прошлом уроке.

Автозагрузка

Сервисы для получения погоды или почты имеет смысл помещать в автозагрузку. Для этого нам надо создать BroadcastReceiver, настроить его IntentFilter на Action = android.intent.action.BOOT_COMPLETED, и добавить права android.permission.RECEIVE_BOOT_COMPLETED. Этот BroadcastReceiver будет вызван системой при старте системы и в нем мы кодим запуск сервиса.

Допустим, есть проект с сервисом MyService.

Создаем в проекте класс MyBroadReceiv

В манифесте добавляем его как Receiver и настраиваем фильтр

Добавляем права на получение сообщения о загрузке

Инсталлим проект на AVD. Закрываем AVD. Запускаем через меню в Eclipse: Window > AVD Manager. Находим там наш эмулятор и запускаем вручную.

Когда он запустился, смотрим логи

onReceive android.intent.action.BOOT_COMPLETED
MyService onCreate
MyService onStartCommand

Сработал BroadcastReceiver и запустил сервис.

Если после запуска AVD логи не отображаются, то откройте DDMS и во вкладке Devices явно выберите ваш AVD.

P.S. Я уже писал об этом, но напишу еще раз. Последующие уроки будут выходить по более свободному графику. Следите за обновлениями.

На следующем уроке:

— создаем свой ContentProvider

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Службы переднего плана

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

Читайте также:  Что будет если удалить систему андроид

Чтобы запустить службу переднего плана, приложение должно отправить цель, которая указывает Android запустить службу. Затем служба должна зарегистрироваться в качестве службы переднего плана с Android. Приложения, работающие на Android 8,0 (или более поздней версии) Context.StartForegroundService , должны использовать метод для запуска службы, а приложения, работающие на устройствах с более старой версией Android, должны использовать Context.StartService

Этот метод расширения C# является примером запуска службы переднего плана. В Android 8,0 и более поздних версиях будет использоваться StartForegroundService метод, в противном случае StartService будет использован более старый метод.

Регистрация в качестве службы переднего плана

После запуска службы переднего плана она должна зарегистрироваться в Android, вызвав StartForeground . Если служба запускается с помощью Service.StartForegroundService метода, но не регистрируется, Android останавливает службу и помечает приложение как неотвечающее.

StartForeground принимает два параметра, оба из которых являются обязательными.

  • Целочисленное значение, уникальное в пределах приложения для обнаружения службы.
  • Notification Объект, который Android будет отображать в строке состояния, пока служба запущена.

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

Этот фрагмент кода является примером регистрации службы в качестве службы переднего плана.

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

строке состояния на

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

Дополнительные сведения об уведомлениях см. в разделе локальные уведомления в статье об уведомлениях Android .

Отмена регистрации в качестве службы переднего плана

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

Отображаемое уведомление о строке состояния также можно удалить, передав true методу:

Если служба остановлена с помощью вызова StopSelf или StopService , уведомление в строке состояния будет удалено.

Источник

Многопоточная разработка для Android, часть 2. Раздаем задачи по сервисам

Содержание статьи

В продолжении темы прошлого номера мы узнаем, каким образом в Android можно генерировать дополнительные потоки и зачем это может понадобиться. Сегодня поговорим о таком компоненте приложения, как сервисы, и опишем их роли в мобильном мире Android. Сервисы и потоки тесно переплетены между собой, поэтому, если ты еще не читал прошлый номер «Хакера», начни с моей статьи, будет полезно!

Сервисы

Компания Google в своей статье Application Fundamentals, обращенной к будущим Android-разработчикам, выделяет четыре базовых компонента Android-приложения: Activity, Service, поставщики содержимого (Content Providers) и приемники широковещательных сообщений (BroadCast Recievers). С Activity начинается знакомство с разработкой, о последних двух компонентах «Хакер» писал раньше (и еще обязательно к ним вернется), поэтому сейчас поговорим о сервисах.

Большинство мобильных устройств обладают достаточно скромными ресурсами, а значит, ОС приходится постоянно перераспределять их между работающими приложениями. Если в системе нет свободной памяти для нового запущенного приложения, то ОС принудительно завершает Activity, которые находятся в состояниях onStop и onPause, а вместе с ними и их дополнительные потоки.

Такое положение дел существенно урезает возможности дополнительных потоков внутри UI — Activity на экране постоянно меняются, а значит, созданные ими потоки не могут жить вечно. Поэтому генерировать дополнительные потоки в Activity целесообразно только в случае, если вычисления завершатся не позже, чем пользователь переключится на другое окно, — в противном случае велик риск потерять результаты или не закончить вычисления.

И тут на помощь приходят сервисы! По сути дела, это те же самые Activity, но без графических элементов. Ввиду отсутствия UI они предназначены для длительных операций, которые могут долгое время выполняться без участия пользователя. Проигрывание музыки, запись данных для фитнес-трекера, продолжительный сетевой обмен — все это задачи для сервисов.

У сервисов в Android существует даже собственный жизненный цикл (lifecycle), который схож с жизненным циклом Activity, и, не привязанные к конкретному Activity, они работают максимально возможное время. ОС начнет убивать сервисы только в экстренных ситуациях, если пользователь параллельно с нашим приложением запустит игру, которая съест вообще все ресурсы и все равно будет тормозить.

Рис. 1. Жизненный цикл сервисов (c) Google

Xakep #209. Capture the Flag

В Android сервисы создаются с помощью класса Service или его наследника IntentService. Несмотря на схожие названия, их функциональные возможности и предназначение серьезно отличаются.

IntentService

Наиболее простой способ создать свой сервис — это воспользоваться классом IntentService. Созданный с его помощью сервис запустится в новом потоке, выполнит все необходимые действия, после чего будет остановлен системой.

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

Полезная нагрузка размещается в методе onHandleIntent() , который будет выполнен системой сразу после старта сервиса. После завершения работы этого метода сервис будет остановлен системой, а ресурсы освобождены.

Передача данных, как и запуск самого сервиса, происходит через уже известный механизм намерений (Intent).

Любой сервис, как и Activity, необходимо зарегистрировать в манифест-файле, иначе ОС о нем не узнает и он просто не будет запущен.

Все готово! После выполнения метода startService в работающем приложении появится новый поток, который выполнит необходимые действия, не загружая UI. Но простота реализации приносит и свои минусы:

  • Отсутствует явная связь с главным потоком. После старта сервис начинает жить своей жизнью, и вернуть результаты вычислений обратно в Activity можно только с помощью широковещательных сообщений.
  • IntentService подходит для выполнения редких, разовых операций, поскольку его конструкция не позволяет выполнять несколько задач одновременно. Если IntentService уже чем-то занят, то при повторном запуске сервиса будет организована очередь и вычисления будут выполнены последовательно.
  • Операции, выполняемые в IntentService, не могут быть прерваны, а значит, сервис будет висеть в памяти до тех пор, пока не завершатся все действия, задуманные в методе onHandleIntent .
Читайте также:  Можно ли установить прошивку с другого андроида

Service

А теперь настало время познакомиться с родителями :). Я сознательно начал разговор с наследника — IntentService, поскольку он предельно прост в эксплуатации: просит мало, работает долго и уходит незаметно. С «оригинальным» же Service все сложнее, чем кажется поначалу.

В статье Services, где разработчики Google рассказывают о своем детище, он назван компонентом, который позволяет выполнять длительные операции в фоне. Прочитав вступление, испытываешь огромный соблазн сразу же накидать сервисов в приложение и выкатить релиз. Но не все так просто — к примеру, портал Stack Overflow завален вопросами вроде «почему мои сервисы постоянно выдают ANR-сообщения?».

Рис. 2. Более 60 тысяч вопросов по сервисам в Android

Оказывается, изначально объект класса Service не создает для себя новый поток, а выполняется там, где его инициализировали! Поэтому, создав в MainActivity новый сервис, разработчик довольно быстро подвесит UI. Чтобы этого избежать, необходимо внутри сервиса самостоятельно генерировать потоки (AsyncTask, Loader и так далее — выбирай любой) и помещать ресурсозатратные вычисления в них.

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

Недавно «Хакер» рассказывал, что в Android достаточно легко написать зловред, который бы шифровал пользовательские данные стойким ключом. Тогда мы не акцентировали свое внимание на производительности — файлы шифровались последовательно, один за другим. Сейчас посмотрим, как с помощью сервиса можно сократить время выполнения операций шифрования.

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

Количество доступных нам потоков заранее неизвестно — это во многом зависит от ресурса устройства. Но бояться ошибок переполнения не стоит, за генерацией новых потоков следит класс ThreadPoolExecutor. Он самостоятельно выставляет лимиты по операциям, и в случае необходимости новые задачи просто встанут в очередь.

Создавать новые потоки будем с помощью классов Handler и Looper, которые нам уже знакомы по первой части статьи.

По умолчанию сервис живет в потоке породившего его Activity. Нас это не очень устраивает, поэтому в методе OnCreate нужно породить новый поток.

Теперь сервис отвязан от Activity и будет жить своей жизнью. Основная логика создаваемого сервиса содержится в методе onStartCommand , который будет выполняться каждый раз при вызове метода startService .

Каждый сервис существует в единственном экземпляре, поэтому многократный вызов метода startService не размножает экземпляры сервиса, а только создает очередь из заданий для него. И если IntentService просто выполнит все задания последовательно, одно за другим, то благодаря классу Service у нас появляется возможность запустить все задачи одновременно в независимых потоках. Достаточно в методе onStartCommand создавать новые потоки для каждой задачи.

Остановка сервиса

Сервис, созданный с помощью одноименного класса, будет жить, пока его принудительно не остановят. Сделать это можно либо откуда-то снаружи методом stopService , с указанием интента, которым он был запущен, либо внутри самого сервиса методом stopSelf .

Как узнать, что сервис уже все сделал, особенно если мы поставили перед ним сразу несколько задач? В этом нам поможет параметр startId у метода onstartcommand — это порядковый номер каждого вызова сервиса, который увеличивается на единицу при каждом запуске.

Создав новый поток, разумно завершать его методом stopSelf , указывая startId в качестве параметра.

С таким параметром ОС не будет сразу завершать сервис, а сравнит переданный идентификатор с тем, который был выдан при последнем запуске onStartCommand . Если они не равны, значит, в очередь была добавлена новая задача и сервис остановлен не будет.

Параметры перезапуска

Сервис — важный компонент приложения, и от стабильности его работы может зависеть стабильность всего приложения. Именно поэтому, даже если ОС и внештатно выгрузит сервис из памяти, есть возможность его запустить заново, как только появятся свободные ресурсы.

Метод onStartCommand возвращает переменную, указывающую ОС, как следует поступить с сервисом, если он был принудительно остановлен. К счастью, разработчикам не нужно запоминать сами числа, в классе Service есть их мнемонические обозначения.
Существует три варианта того, как ОС может поступить с сервисом, если он был принудительно завершен.

START_NOT_STICKY — не будет перезапущен системой, и все останется так, как есть. Подходит для случая, когда он выполняет не самые важные задачи, и приложение может позже при необходимости самостоятельно его перезапустить.

START_STICKY — будет запущен заново, но в Intent, который ОС создаст для его запуска, не будет никаких параметров. Такой вариант работает с аудиоплеерами — он должен быть активным в фоне, но не обязательно при этом автоматически начинать проигрывать песни.

START_REDELIVER_INTENT — сервис будет запущен с теми же параметрами, которые были при его последнем старте. Это удобно, когда в фоне загружается большой файл и его загрузка была прервана.

Bound Service

Иногда бывает необходимо выполнить большой объем черновой работы, а потом несколько раз использовать полученные результаты. К примеру, это может быть загрузка из сети большого XML-файла с последующим парсингом данных. Конечно, можно как-то кешировать результаты, а затем при необходимости их заново подгружать. Но есть вариант получше — создать сервис, который выполнит необходимые расчеты, раздаст данные всем желающим и завершит свою работу.

Читайте также:  Офис для телефонов с андроидом

Описанный алгоритм мы можем организовать уже имеющимися средствами, но за одним исключением — непонятно, когда услуги сервиса уже больше не понадобятся и его можно остановить. Эту проблему возможно решить, создав привязанный (bound) service — это сервис, который будет работать до тех пор, пока к нему привязан хотя бы один Activity или другой сервис. В случае если «клиентов» у такого сервиса нет, он автоматически останавливается, но при необходимости может быть снова запущен.

Привязанный сервис создается с помощью того же класса Service, но теперь необходимо инициализировать метод OnBound .

IBinder — это интерфейс, позволяющий организовать связь между двумя различными потоками, функционирующими внутри одного процесса. Для его использования не нужно самостоятельно реализовать этот интерфейс, достаточно создать объект класса Binder. Сейчас его основной задачей будет предоставление ссылки на работающий сервис в Activity.

Чтобы Activity могли подключаться к сервису, разработчики Google создали класс ServiceConnection, основная задача которого — организовывать взаимодействие Activity и сервиса, а также следить, чтобы созданное подключение работало корректно.

Для работы этого механизма необходимо переопределить методы, отвечающие за подключение к сервису и отключение от него.

При успешном подключении к сервису в потоке Activity появляется экземпляр сервиса, к которому произошло подключение. Индикатором состояния подключения к сервису будет булева переменная.

Процедуры подключения к сервису и отключения от него рекомендуется выполнять в методах onStart и onStop . Это разумно, поскольку процедуры запуска/остановки сервиса и подключения к нему достаточно ресурсозатратны и не должны выполняться слишком часто.

Для получения данных с сервиса достаточно обратиться к нему через его экземпляр, полученный благодаря ServiceConnection.

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

В этом случае стоит вызывать методы сервиса в дополнительных потоках, например с помощью уже знакомого нам AsyncTask и слабых ссылок.

До тех пор пока сервис привязан к какому-либо Activity, его данные будут сохраняться, а значит, ими смогут воспользоваться и другие компоненты приложения. В этом и есть плюс привязанных сервисов — достаточно один раз загрузить и обработать данные, а затем готовые результаты смогут легко получить остальные участники приложения.

Foreground Service

В некоторых случаях необходимо сделать критически важный сервис, который будет выгружен из системы только вместе с выключением устройства. Именно такими сервисами являются аудиоплеер или GPS-трекер. Они будут работать всегда, даже когда создавшее их приложение будет завершено, и ни одно другое приложение не сможет вытеснить их из памяти. Если пользователь решил послушать новый альбом Taylor Swift, то песни в наушниках перестанут играть только в двух случаях — кто-то нажмет кнопку «Стоп» или в устройстве сядет аккумулятор.

Это так называемый сервис переднего плана (foreground service), и его можно создать как из класса Service, так и из класса IntentService. Но есть небольшое условие: в панели уведомлений на протяжении всей работы сервиса должна быть соответствующая информация.

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

Создаем наследующий Service класс, который реализует интерфейс MediaPlayer.onPreparedListener .

Старт сервиса, как всегда, организуем через Intent, имя проигрываемого файла можно передать через его параметры.

Когда песня будет готова к проигрыванию, в сервисе сработает метод onPrepared из реализуемого интерфейса.

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

Рис. 3. Неубиваемое уведомление от привязанного сервиса

Скорее всего, пользовать захочет нажать на выводимое уведомление и попасть в Activity приложения, где можно будет поменять музыку. С этим справится PendingIntent, способный создать новый стек.

Теперь нужно связать сформированный стек и уведомление. А вывод информации о работающем сервисе выполняется методом startForeground с указанием уникального идентификатора.

Что же выбрать?

Итак, за два месяца мы с тобой довольно подробно поговорили о том, где же можно обработать данные для UI. Давай теперь кратко просуммируем то, что у нас есть для создания многопоточности в Android.

Потоки, созданные в Activity, подходят для сравнительно недлительных действий, результат которых важен только в момент жизни конкретного Activity. Их можно создать с помощью AsyncTask, AsyncTaskLoader или их аналогов — все зависит от того, как тебе будет удобней.

Сервисы существуют в приложении на равных правах с Activity, но не всегда в отдельном потоке. Они могут распараллелить задачи и выполнять громоздкие вычисления долгое время, пока на экране будут меняться Activity. IntentService завершит себя сам, когда выполнит все задачи, а Service будет висеть в фоне. Применяя сервисы, нужно быть внимательным, он может случайно оказаться в потоке UI или долго и без дела висеть в памяти, забирая ресурсы.

Заключение

Надеюсь, про многопоточность было сказано достаточно, чтобы ты понял ее необходимость и полезность. Вся ли это информация? Конечно, нет, Android — очень сложная ОС, которая постоянно развивается, и нельзя в двух статьях разобрать абсолютно все нюансы такой важной темы. В конце концов, главное — как приложение работает, а не то, как оно написано. Если будут вопросы, обязательно мне пиши. Удачи!

Источник

Оцените статью