Полный список
— передаем данные в сервис
— рассматриваем методы остановки сервиса stopSelf и stopSelfResult
В прошлом уроке мы использовали в коде метод stopSelf. Он вызывается внутри сервиса и останавливает этот сервис. У этого метода есть еще пара вариаций: stopSelf(int startId) и stopSelfResult(int startId). Как видим, на вход они требуют некий startId. Что это и откуда его взять? Для ответа на вопрос давайте взглянем на входящие параметры метода onStartCommand:
Первый – это Intent. Тот самый, который отправляется в путь, когда мы стартуем сервис с помощью метода startService. Соответственно вы можете использовать его для передачи данных в ваш сервис. Тут все аналогично, как при вызове другого Activity – там вы тоже можете передать данные с помощью intent.
Второй параметр – флаги запуска. Он нам пока не нужен, пропускаем его.
Третий параметр – startId. Простыми словами – это счетчик вызовов startService пока сервис запущен. Т.е. вы запустили сервис методом startService, сработал метод onStartCommand и получил на вход startId = 1. Вызываем еще раз метод startService, сработал метод onStartCommand и получил на вход startId = 2. И так далее. Счетчик сбрасывается, когда сервис будет остановлен методами stopService, stopSelf и пр. После этого вызовы снова идут с единицы.
Именно этот startId и нужен на вход методу stopSelf(startId). Т.е. этот метод дает системе понять, что конкретный вызов сервиса был успешно обработан. И мне кажется логичным, что, когда все вызовы сервиса выполнили метод stopSelf(startId) и система видит, что не осталось необработанных вызовов – сервис можно останавливать. Т.е. поступила куча вызовов, они все обработались и когда последний обработанный (но при этом не обязательно последний поступивший) выполняет stopSelf(startId) – сервис останавливается.
Но на самом деле алгоритм чуть другой. Сервис останавливается, когда последний полученный (а не последний обработанный) вызов выполняет метод stopSelf(startId). А при этом могут продолжать работать ранее полученные вызовы. Почему так сделано – я не знаю.
В общем, наверно сумбурно объяснил, сейчас на примерах все станет понятнее.
А метод stopSelfResult(startId) аналогичен методу stopSelf(startId), но при этом еще возвращает boolean значение – остановил он сервис или нет.
Project name: P0931_ServiceStop
Build Target: Android 2.3.3
Application name: ServiceStop
Package name: ru.startandroid.develop.p0931servicestop
Create Activity: MainActivity
Добавим в strings.xml строки:
Только кнопка для запуска сервиса.
Создаем сервис MyService.java:
Скажем прямо, тут немного нетривиально получилось 🙂 , особенно если опыт работы в Java небольшой. Но попробуем разобраться.
В onCreate создаем некий объект someRes. Это моя выдумка, на примере этого объекта я попробую показать, какую нехорошую ситуацию с ресурсами можно получить с применением метода stopSelf(startId). Этот объект будет использоваться сервисом в обработках вызовов. Пока что на него можно особо не смотреть.
Executors.newFixedThreadPool(1) – эта строка дает нам объект (я буду называть его — экзекьютор), который будет получать от нас задачи (Runnable) и запускать их по очереди в одном потоке (на вход ему мы передаем значение 1). Он сделает за нас всю работу по управлению потоками.
В onDestroy обнуляем someRes.
В onStartCommand мы читаем из intent параметр time. Создаем Runnable-объект MyRun, передаем ему time и startId и отдаем этот объект экзекьютору, который его запустит в отдельном потоке.
MyRun – Runnable-объект. Он и будет обрабатывать входящие вызовы сервиса. В конструкторе он получает time и startId. Параметр time будет использован для кол-ва секунд паузы (т.е. эмуляции работы). А startId будет использован в методе stopSelf(startId), который даст сервису понять, что вызов под номером strartId обработан. В лог выводим инфу о создании, старте и завершении работы. Также здесь используем объект someRes, в лог просто выводим его класс. Если же объект = null, то ловим эту ошибку и выводим ее в лог.
В общем, получилась, на первый взгляд, непонятная мегамонстроконструкция, но я реально не смог придумать ничего проще, чтобы наглядно показать тему урока. Сейчас все это взлетит и станет понятнее )
Почему вообще я использую потоки, я показал в прошлом уроке. Т.к. сервис работает в том же потоке, что и приложение и соответственно будет тормозить экран своей работой.
Да, и не забудьте прописать сервис в манифесте!
По нажатию на кнопку мы отправляем вызов в сервис три раза. И в intent помещаем параметр time.
Соответственно в сервисе будет три раза выполнен метод onStartCommand. Будет создано и передано экзекьютору три MyRun-объекта. Он их по очереди начнет выполнять. Это займет у него соответственно 7,2 и 4 секунд (время паузы мы передаем в intent-е). В конце обработки каждого MyRun будет выполняться stopSelf(startId).
Все сохраняем и запускаем приложение.
Жмем Start, открываем логи и ждем секунд 15. Все должно закончиться строкой MyService onDestroy
Давайте разбирать, чего получилось.
MyService onStartCommand
MyRun#1 create
MyService onStartCommand
MyRun#2 create
MyService onStartCommand
MyRun#3 create
Три раза выполнился метод onStartCommand, т.к. мы в приложении три раза вызвали startService. В каждом вызове создалось по одному объекту MyRun. После символа # идет значение startId. Т.е. видим, что счетчик вызовов работает и startId увеличивается на единицу при каждом вызове.
Все MyRun объекты были переданы экзекьютору, и он начинает их обрабатывать по очереди в одном потоке.
MyRun#1 start, time = 7
MyRun#1 someRes = class java.lang.Object
MyRun#1 end, stopSelf(1)
Первый готов. Метод stopSelf(1) вызван, сервис продолжает жить.
MyRun#2 start, time = 2
MyRun#2 someRes = class java.lang.Object
MyRun#2 end, stopSelf(2)
Второй готов. Метод stopSelf(2) вызван, сервис продолжает жить.
MyRun#3 start, time = 4
MyRun#3 someRes = class java.lang.Object
MyRun#3 end, stopSelf(3)
MyService onDestroy
Третий готов. Метод stopSelf(3) вызван и сервис после этого остановлен (MyService onDestroy), т.к. для последнего поступившего вызова (номер 3 в нашем случае) был вызван stopSelf(startId).
Но, как я написал в начале урока, мне кажется странным останавливать сервис, если закончена обработка последнего поступившего вызова. В этом примере у нас вызовы обрабатывались по очереди и проблем не возникло. Т.е. последний поступивший вызов обрабатывался последним и его stopSelf(startId) останавливал сервис. Но, что если вызовы будут обрабатываться параллельно? И последний поступивший вызов выполнит stopSelf(startId), а первый поступивший еще будет работать? Проверим.
В MyService.java в методе onCreate в строке создания экзекьютора поменяйте 1 на 3. Должно получиться так:
Теперь наш экзекьютор будет выполнять поступающие ему задачи не в одном, а в трех разных потоках. Как говорил один полосатый кошак из Простоквашино: «я и так счастливый был, а теперь в три раза счастливее стал, потому что у меня теперь три потока есть» 🙂 Соответственно, три наших вызова будут теперь обработаны параллельно, а не по очереди. И пришло время обратить внимание на someRes.
Все сохраним и запустим, жмем Start. На этот раз все заняло не 15 сек, а 7. Последняя строка логов должна быть такой: MyRun#1 end, stopSelf(1)
Разбираемся в логах.
MyService onStartCommand
MyRun#1 create
MyService onStartCommand
MyRun#2 create
MyRun#1 start, time = 7
MyService onStartCommand
MyRun#3 create
MyRun#2 start, time = 2
MyRun#3 start, time = 4
(у вас может быть немного иной порядок строк)
Три раза выполнился метод onStartCommand, т.к. мы в приложении три раза вызвали startService. В каждом вызове создалось по одному объекту MyRun и все они сразу начали работать, потому что экзекьютор теперь раскидал их по трем потокам и им не надо ждать друг друга.
Судя по параметру time, который мы используем для паузы, MyRun#2 закончит работать первым (через 2 сек.), MyRun#3 – вторым (через 4 сек.), MyRun#1 – третьим (через 7 сек.). Так и происходит.
MyRun#2 someRes = class java.lang.Object
MyRun#2 end, stopSelf(2)
Объект someRes доступен. Вызов с startId = 2 обработан. stopSelf(2) вызван – сервис продолжает жить.
MyRun#3 someRes = class java.lang.Object
MyRun#3 end, stopSelf(3)
MyService onDestroy
someRes доступен. Вызов с startId = 3 обработан. stopSelf(3) вызван – сервис остановлен (MyService onDestroy). Тут все верно — последний поступивший вызов имеет startId = 3, следовательно, вызов метода stopSelf(3) останавливает сервис. Следовательно, выполнился метод onDestroy, а в нем обнулился someRes.
MyRun#1 error, null pointer
MyRun#1 end, stopSelf(1)
someRes недоступен, т.к. он был обнулен чуть раньше в onDestroy. Ну и вызов метода stopSelf(1) уже бесполезен. Сервис был официально остановлен ранее.
Т.е. получилась неприятная ситуация, когда мы в onDestroy освободили объект, а он был еще нужен. А ведь вместо someRes мог быть объект по работе с БД. Здесь надо быть аккуратным, понимать механизм работы stopSelf(startId) и, когда именно этот метод остановит сервис.
Ну и напоследок рассмотрим метод stopSelfResult(startId). Напомню, что он полностью аналогичен методу stopSelf(startId), и при этом возвращает boolean-значение, остановлен сервис или нет после вызова этого метода.
В MyService.java в классе MyRun перепишем метод stop:
Мы выводим в лог результат вызова метода stopSelfResult.
Сохраняем, запускаем и жмем Start.
MyService onCreate
MyService onStartCommand
MyRun#1 create
MyService onStartCommand
MyRun#2 create
MyRun#1 start, time = 7
MyService onStartCommand
MyRun#3 create
MyRun#2 start, time = 2
MyRun#3 start, time = 4
MyRun#2 someRes = class java.lang.Object
MyRun#2 end, stopSelfResult(2) = false
MyRun#3 someRes = class java.lang.Object
MyService onDestroy
MyRun#3 end, stopSelfResult(3) = true
MyRun#1 error, null pointer
MyRun#1 end, stopSelfResult(1) = false
Видим, что stopSelfResult(3) вернул true, и тем самым сообщил нам, что именно он остановил сервис. А stopSelfResult(1) и stopSelfResult(2) вернули false – их вызов не привел к остановке сервиса.
Понимаю, что сложно это все, но надеюсь, что у меня получилось тему раскрыть. Если остаются сомнения, попробуйте поиграться экзекьютором, кол-вом потоков и вызовов, и параметром time – должно стать понятнее.
А вообще, до тех пор, пока вам это все не понадобится в боевых условиях, вполне можно понимать это не до конца.
На следующем уроке:
— рассмотрим подробнее метод onStartCommand
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Служба Android останавливается при закрытии приложения
Я запускаю сервис из своей основной активности Android следующим образом:
Когда я закрываю страницу активности, вытаскивая ее из списка последних приложений, служба перестает работать и перезапускается через некоторое время. Я не могу использовать постоянные службы с уведомлениями из-за требований к моему приложению. Как я могу заставить службу НЕ перезапускать или выключать и просто продолжать работу при выходе из приложения?
14 ответов
Я в той же ситуации, пока я узнал, когда приложение закрыто, сервис закрывается также потому, что он находится в одном потоке, поэтому служба должна быть в другом потоке, чтобы она не закрывалась, посмотрите на это и посмотрите, как поддерживать сервис с помощью диспетчера аварийных сигналов здесь пример http://www.vogella.com/articles/AndroidServices/article.html таким образом, что ваша служба не будет отображаться в уведомлении.
Наконец, после всех исследований, которые я сделал, я понял, что лучший выбор для долгого обслуживания — startForeground() , потому что он сделанные для этого, и система действительно эффективно работает с вашим сервисом.
сделайте так, чтобы вы обменивались данными в своем «Мастере»
то ваша служба будет запущена в другом процессе с именем ServiceProcess
если вы хотите, чтобы ваша служба никогда не умирала:
onStartCommand() вернуться START_STICKY
создать службу Deamon
jin → создать процесс Native Deamon, вы можете найти некоторые проекты с открытым исходным кодом на github
startForeground(), есть способ запуститьForeground без уведомления, google it
Службы иногда довольно сложны.
Когда вы запускаете службу из какого-либо действия (или вашего процесса), служба, по сути, работает в одном процессе.
цитата из примечаний разработчика
Большая путаница в отношении класса Service фактически вращается вокруг того, что это не так:
Служба не является отдельным процессом. Сам объект службы не подразумевает, что он работает в своем собственном процессе; если не указано иное, он работает в том же процессе, что и приложение, в котором оно входит.
Служба не является нитью. Это не значит, что нужно выполнять работу основного потока (чтобы избежать ошибок, связанных с ошибками приложения).
Итак, что это означает, если пользователь удалит приложение из последних задач, он удалит ваш процесс (включая все ваши действия и т.д.). Теперь давайте рассмотрим три сценария.
Сначала, где служба не имеет уведомления переднего плана.
В этом случае ваш процесс будет убит вместе с вашим сервисом.
Второй, где служба имеет уведомление переднего плана
В этом случае служба не будет убита, и ни один из них не будет выполняться
Третий сценарий Если служба не имеет уведомления переднего плана, она все равно может работать, если приложение закрыто. Мы можем сделать это, запустив службу в другом процессе. (Тем не менее, я слышал, что некоторые люди говорят, что это может не сработать, вам нужно попробовать сами)
вы можете создать службу в отдельном процессе, включив атрибут ниже в вашем манифесте.
Android: процесс = «: yourService»
android: process = «yourService» имя процесса должно начинаться с нижнего регистра.
цитата из примечаний разработчика
Если имя, присвоенное этому атрибуту, начинается с двоеточия (‘:’), новый процесс, частный для приложения, создается, когда это необходимо, и служба запускается в этом процессе. Если имя процесса начинается с символа с нижним регистром, служба будет выполняться в глобальном процессе этого имени при условии, что у него есть разрешение на это. Это позволяет компонентам в разных приложениях совместно использовать процесс, уменьшая использование ресурсов.
вот что я собрал, если кто-то эксперт, пожалуйста, исправьте меня, если я ошибаюсь:)
Источник