- Android programmers blog
- Поиск по этому блогу
- понедельник, 5 октября 2015 г.
- Callback’и в Android
- Русские Блоги
- Разговор об Android callback-CallBack
- Во-первых, прийти к выводу
- Во-вторых, обратные вызовы, которые можно увидеть везде
- 3. Попробуйте написать обратный звонок самостоятельно
- Пример 1
- Погружение в службы Android
- Введение
- Потоки, службы и жизненный цикл компонентов Android
- Изменения в Android O
- Запущенные службы
- Intent
- Передний план и механизм постоянного уведомления
- Остановка запущенных служб
- Привязанные службы
- bindService() и onCreate()
- Привязка службы
- Отвязка от службы и вызов onDestroy()
- Привязанные и запущенные службы одновременно
- Переход в запущенное состояние
- Завершение работы службы и отвязывание
- Примеры
Android programmers blog
Simply about difficult
Поиск по этому блогу
понедельник, 5 октября 2015 г.
Callback’и в Android
Еще решил написать одну заметку по поводу коллбеков. Штука интересная и полезная, без нее редко когда получается что то разумное написать. По этому решил что надо будет написать пару примеров по работе с колбеками.
Для начала хочу сказать что есть разные библиотеки типа EventBus и OttoBus которые сделают всю работу за вас и еще больше сделают… Но иногда эти библиотеки сильно много делают для обычной тривиальной задачи, например вернуть респонс из AsyncTask’a или еще что-то похожее, из-за этого использовать эти библиотеки не целесообразно и достаточно написать один интерфейс и вызывать его в нужном месте по тому или иному событию. Собственно это я сегодня и хочу продемонстрировать на примере приложения.
У нас будет небольшой апп который будет тянуть список спорт. каналов через AsyncTask, парсить то что вернет сервер с помощью кастомного JsonArray и JsonObject и отдавать это все колбеком в активити для отображения в списке.
Для начала давайте сделаем небольшую настройку. Для работы с интернетом нам нужен премишен в манифесте, давайте его добавим.
И сделаем разметку с списком, все будет елементарно и просто.
Все, с настройками мы закончили, теперь давайте кодить!
Первый вариант Callback’a, получение данных в Activity
Первый вариант у нас будет такой как я описал выше, он имеет вот такую архитектуру:
Это очень краткий пример того что будет происходить. Создаем интерфейс, далее в классе который нам должен что-то вернуть вызываем этот колбек что бы можно было отследить когда он выполнился, а после этого отображаем то что вернул колбек в нашей активити которая включает в себя собственно этот колбек.
Очень сумбурно, нам нужно более детальное рассмотрение этого способа по этому начну.
У вас должен уже быть создан пустой проект с MainActivity, нам она пока не понадобится, мы ее будем использовать в в самом конце.
Сейчас создайте новый класс который будет называться BackgroundTask, в этом методе у нас будет AsyncTask который у нас будет хватать json строку с удаленного сервера.
Вообще использовать AsyncTask не советую, для таких нужд есть прекрасная библотека Retrofit или Volley или еще куча разных великолепных библиотек которые делают запросы и не валятся при повороте экрана. По этому это чисто пример, так как я сделал запрос лучше не делать, повторюсь, лучше используйте готовые решения.
Вот как то так у нас будет работать наш AsyncTask, но пока что вам не хватает нескольких классов которые выполняют кое какую работу, давайте их напишем что бы все заработало. Нам не хватает интерфейса ResponseCallback, так давайте создадим его.
Так же нам не хватает ChannelsModel, давайте и его создадим.
А еще нам не хватает ChannelsParser который парсит нашу json возвращенную с сервера. Держите, мне не жалко!
Вот! Вроде бы картинка у нас обрисовалась и все нужные классы были созданы. Теперь нам нужно все это собрать в одно, отобразить наш заветный список на экране. Разметка у нас уже готова по-этому нам осталось сделать адаптер и вызвать его в MainActivity. Вот это мы сейчас и сделаем, создаем еще один класс ChannelsAdapter и заполняем его.
Так, тут все просто я думаю все сталкивались с кастомными адаптерами по этому сильно вникать не буду. Если же не сталкивались то у меня в блоге есть статья где я описываю создание кастомного адаптера. Вернемся к функциям, в адаптере мы принимаем ArrayList с данными и отображаем его в TextView который я беру из ресурсов android’a. Собственно и все. Давайте уже наконец закончим и вызовем это все в MainActivity.
Вот так вот просто на примере можно увидеть как работают коллбеки. Но это не все, еще есть второй вариант колбеков которые работают через Setters и Getters. Я на примере этой же программы покажу как это делается, там так же ничего сложного нет.
Второй вариант Callback’a, работа через setters и getters
Как и с первым вариантом во втором я приведу абстрактный пример того как он должен выглядеть в кратце.
Интерфейс можно создавать где угодно и как угодно, по этому не обязательно его выносить в отдельный класс, можно создать как внутри активити или асинк таска так и внутри адаптера и любого другого класса. Создается интерфейс, объявляется, дальше вызывается его инстанс, то есть метод который должен отработать во время евента. А потом в Activity или Fragment’e идет вызов этого метода и получение нужных данных из него. В общем пример опять сумбурный, сейчас на примере нашей программы сделаем и будет все понятно.
Для начала в нашем адаптере нужно создать интерфейс. В самом низу класса пишем вот такое:
Дальше нам надо создать setter для этого интерфейса что бы можно было вызывать его во время создания адаптера и передавать туда наш колбек.
Теперь в getView пишем onClickListener для создания евента клика по айтему:
Вот так должен выглядеть адаптер в целом
А теперь нам нужно перенести в MainActivity этот функционал. Для этого нам нужно присвоить адаптеру новый сеттер и передать туда новый объект нашего коллбека.
В целом MainActivity должна выглядить таким образом
Вот так вот при использовании коллбеков можно достичь дзена. Надеюсь все было понятно так как пока я это писал меня двадцать пять раз отвлекли и я все эти разы терял мысль по этому возможно что то может быть черезчур запутано, спрашивайте если что то не понятно. В любом случае есть исходники рабочие, по ним так точно можно разобраться (:
Источник
Русские Блоги
Разговор об Android callback-CallBack
Эта статья из короткого книжного блога «Амин Кирен», пожалуйста, укажите источник для перепечатки или цитирования.
Я улыбаюсь, я всегда считал, что в разработке программного обеспечения не так уж много редких вещей. Первоначальное намерение иностранных экспертов в области дизайна также просто и красиво, но в процессе передачи знаний, перевода и личного понимания это привело к недоразумениям.
Первоначальный вид языка программного обеспечения должен быть простым и красивым.
Несколько дней назад студент из университета спросил меня, что происходит с обратным вызовом, и это было основной причиной, по которой я хотел написать эту статью. Я не хочу накапливать принципы и невнятные слова, а просто описать это так, как мне кажется, легче понять.
Помните, что при изучении обратных вызовов я всегда чувствую, что обратные вызовы — это странная вещь. Действительно, вам нужно идти глубже. Есть действительно много вещей, которые нужно сказать, но как разработчик, я чувствую, что у меня есть определенный технический момент или пункт знаний. Простое и хорошо понятое познание очень важно, и необходимо сказать, что очень важно иметь общее познание.
Во-первых, прийти к выводу
Обратный вызов означает, что A и B имеют отношения сотрудничества. Для определенной части A требуется, чтобы B сам сказал A, что делать. Это обратный вызов. Обратный вызов должен иметь интерфейс)
Во-вторых, обратные вызовы, которые можно увидеть везде
Мы знаем, что обратные вызовы можно увидеть повсюду в Android, например, событие нажатия кнопки
.
.
Далее, давайте посмотрим на исходный код setOnClickListener, а затем посмотрим на замечания
Другие Google сказали, что этот метод является обратным вызовом, они являются обратным вызовом
Обратный вызов должен иметь интерфейс
.
.
(в основном все виды setOnClickListener являются обратными вызовами, а все виды слушателей являются обратными вызовами)
Прочитав это, давайте кратко подтвердим наш вывод.
Пожалуйста, сделайте вывод:
Обратный вызов означает, что A и B имеют отношения сотрудничества. Определенная связь A требует, чтобы B сказал A, что делать. Это обратный вызов (обратный вызов должен иметь интерфейс
В упомянутом выше небольшом примере View — это то, что мы называем A, а наша MainActivity — это то, что мы называем B (TextView наследует от View, мы все знаем), затем мы нажимаем B и хотим сделать что-то Только B знает это, A определенно не знает, что B хочет делать, и тогда A должен иметь свою собственную логику, чтобы просто работать в соответствии с B, так что, ах, это . «Определенная связь A требует, чтобы B сказал A. Как сделать»
Поскольку в A есть метод, который можно вызывать обратно, в A. должен быть интерфейс.
3. Попробуйте написать обратный звонок самостоятельно
Пока вы написали пользовательский элемент управления, вы должны написать обратный вызов. Это естественно, и смысл этой статьи — просто описать понимание обратных вызовов.
На самом деле, делать обратные вызовы очень хорошо, например, мы сделали пользовательский индикатор выполнения, кнопку или переключатель и т. Д. Нам всем нужно создать интерфейс обратного вызова. Каковы преимущества? Люди, которые используют наши элементы управления, могут сами выбирать некоторые вещи, вместо того, чтобы писать интерфейс, мы можем предоставить некоторые методы, чтобы другие могли выбрать цвет, размер, значение и так далее.
Пример 1
Мы предполагаем, что сделали пользовательскую кнопку переключения, а затем нажимаем ее, чтобы изменить текст другого элемента управления. (Только для демонстрации, без переключателя, чтобы не делать код слишком длинным)
После щелчка вызывающая сторона должна выполнить некоторую логику. Что касается конкретной логики, которая должна быть выполнена, это совсем не наше дело. Это то, что хочет делать человек, использующий наш контроль.
Простой пользовательский контроль
Как и выше, демонстрация приложения обратного вызова завершена.
Конечно, вы также можете сказать, что я напрямую использую ваш mTog для вызова метода setOnClickListener в MainActivity. Разве это не та же функция, что и выше! Да, верно, но мы здесь не для реализации функций, а для того, чтобы кратко описать, как определить интерфейс для реализации обратных вызовов.
Кроме того, как и выше, является демонстрацией корректировки порядка, и немного дальше, мы, конечно, можем также добавить параметры в onDoClick при написании интерфейса, такие как void onDoClick (DiyToggle diyToggle, Boolean toggleState); Таким образом, мы можем указать, какой DiyToggle становится каким состоянием переключения.
.
.
нота
Последняя работа — DiyToggle, а почему A в конце может изменить текст, это собственное системное событие onTouchEvent со сложным дизайном Android, благодаря которому DiyToggle может делать очень много вещей.
Код обычно выполняется по порядку. Нам нужно объяснить mTog, который реализует интерфейс в MainActivity, то есть метод onDoClick подходит для DiyToggle Положение, чтобы после вызова mTog метода setToggleClickListener была выполнена учетная запись mTog.
Источник
Погружение в службы Android
Перевод статьи «Deep Dive into Android Services» от Nazmul Idris. Я оставил оригинальное название автора, хотя это скорее не «погружение», а «знакомство». Думаю, текст будет полезен начинающим разработчикам. Статья отлично дополняет офф. документацию по службам на Android. В статье разбираются особенности взаимодействия с запущенными и привязанными службами. Плюс статьи в том, что учитываются изменения в работе со службами в Android O. В переводе есть незначительные, по сравнению с оригиналом, изменения, добавленные для пущей ясности.
Введение
Большинство современных android-приложений выполняют часть задач в фоне. Это означает, что задачи выполняются в фоновом потоке, а не в потоке пользовательского интерфейса (UI-поток).
Если вы создаете Thread (поток) или Executor (обертка управления потоками) в конкретной Activity своего приложения, то это может привести к непредсказуемым результатам. Например, во время простой смены ориентации экрана, ваша Activity создается заново и потокам, привязанным к старой Activity , некуда возвращать результат.
Чтобы справиться с этим вы могли бы использовать AsyncTask . Но что, если вашему приложению необходимо запустить этот фоновый поток не только из Activity , но и из нотификации (notification) или из другого компонента?
В этом случае служба (service) это подходящий компонент Android, который свяжет жизненный цикл потока со своим жизненным циклом, и таким образом не потеряет его.
Служба — это компонент android-приложения без видимого интерфейса, который запускается в основном потоке приложения. Служба должна быть объявлена в манифесте. Если вам необходимо чтобы служба работала в фоновом потоке, вы должны самостоятельно реализовать это.
Термины фон и передний план перегружены, и могут применяться к:
- жизненному циклу компонентов Android
- потокам
В этой статье, по умолчанию будем считать, что термины фон и передний план относятся к жизненному циклу. Но, когда будет идти речь о потоках, мы будем явно говорить фоновый поток или поток переднего плана.
Существует подкласс android-служб, называемый IntentService , который запускает задачи в фоновом потоке «из коробки». Но мы не будем говорить о таких службах в этой статье.
Потоки, службы и жизненный цикл компонентов Android
Давайте сделаем шаг назад и посмотрим на более общую картину того, что должны делать службы. Ваш код, который работает в фоновом потоке, например Thread или Executor , на самом деле не связан с жизненным циклом какого-либо компонента Android. Если мы говорим об Activity , то она имеет конкретную точку запуска и остановки работы, основываясь на взаимодействии с пользователем. Однако эти точки начала и конца работы Activity не обязательно связаны с жизненным циклом Thread или Executor .
Ниже приведены пояснения к основным временным моментам этой диаграммы Гантта. Детали этих моментов (и пояснения к ним) приведены в остальной части статьи.
Метод службы onCreate() вызывается в момент ее создания (путем запуска или привязки к ней).
Затем, через некоторое время, служба запускает Thread или Executor . Когда Thread завершает работу, он дает об этом знать службе, чтобы та могла вызвать метод stopSelf() . Это довольно распространенный шаблон реализации службы.
Код, который вы пишите в ваших Thread или Executor , должен сообщить службе о запуске или остановке фонового потока.
- Когда поток начинает работу, он должен установить начальное состояние сервиса путем вызова startService()
- Когда поток завершает работу, он должен вызвать stopSelf() у службы.
Метод службы onDestroy() вызывается системой только когда вы сообщили службе, что пришло время завершать работу. Служба не знает, что будет происходить в коде ваших Thread или Executor — это зона вашей ответственности. Таким образом, задача программиста сообщить службе о начале и о завершении работы.
Службы делятся на два вида: запущенные и привязанные. Кроме того, служба может быть запущенной и допускать привязку. Мы рассмотрим каждый из случаев:
- Запущенная служба
- Привязанная служба
- Привязанная и запущенная служба одновременно
Изменения в Android O
В Android O (API 26) произошли существенные изменения в регулировании фоновых служб системой. Одно из главных изменений в том, что запущенная служба, которая не в белом списке (в белый список помещаются службы, работа которых видна пользователю; подробнее смотри в офф. документации) или которая явно не сообщает пользователю о своей работе, не будет запускаться в фоновом потоке после закрытия Activity . Другими словами, вы должны создать уведомление (notification), к которому вы прикрепляете запущенную службу. И вы должны запускать службу с помощью нового метода startForegroundService() (а не с помощью startService() ). И, после создания службы, у вас есть пять секунд чтобы вызвать метод startForeground() запущенной службы и показать видимое пользователю уведомление. Иначе система останавливает службу и показывает ANR («приложение не отвечает»). Ниже мы разъясняем эти положения с помощью примеров кода.
Запущенные службы
Запущенные службы начинают свою работу после вызова метода startService(Intent) в вашей Activity или службе. При этом Intent должен быть явным. Это означает, что вы должны явно указать в Intent имя класса запускаемой вами службы. Или, если вам важно допустить некоторую неопределенность в отношении того, какая служба запускается, вы можете предоставить фильтры намерений для ваших служб и исключить имя компонента из Intent, но затем вы должны установить пакет для намерения с помощью setPackage() , который обеспечивает достаточное устранение неоднозначности для целевой службы. Ниже мы приводим пример создания явного Intent :
Чтобы служба стала запущенной, вы должны вызвать startService() с явным намерением. Если вы не сделаете этого, тогда служба не перейдет в запущенное состояние. И, таким образом, она не сможет перейти на передний план, и stopSelf() на самом деле ничего не выполнит.
Итак, если вы не перевели службу в запущенное состояние, вы не сможете прикрепить ее к уведомлению. Это довольно важные вещи, о которых вы должны помнить, когда вам нужно перевести службу в запущенное состояние.
Служба может быть запущена несколько раз. Каждый раз, когда она запускается, вызывается onStartCommand() . Этому методу передается несколько параметров наряду с явным Intent . Даже если вы запускаете службу несколько раз, она вызывает onCreate() только однажды (конечно, если до этого служба уже не была привязана). Чтобы завершить работу, служба должна вызвать stopSelf() . После того, как служба будет остановлена (когда вы остановите ее), и если с ней ничего больше не связано, вызывается onDestroy() . Помните об этом, когда выделяете ресурсы для вашей запущенной службы.
Intent
Для старта запущенной службы необходим Intent . Компонент Android, в котором стартует служба, на самом деле не хранит соединение с ней, и если ему необходимо что-то сообщить запущенной службе, он может запустить ее снова, используя другой Intent . Это главная разница между запущенной и привязанной службой. Привязанные службы со своей стороны реализуют шаблон клиент-сервер. Где клиент (компонент Android UI или другая служба) хранит соединение и может через него вызывать методы непосредственно у службы.
Помните, что в Android O многое изменилось в отношении запущенных служб. Они больше не могут работать достаточно долго в фоне без механизма постоянного уведомления. И метод старта запущенной службы в фоне в Android O — это startForegroundService(Intent) .
Передний план и механизм постоянного уведомления
Запущенная служба может работать на переднем плане. Опять же, термин передний план не относится к тому работает ли служба в фоновом потоке или в главном потоке. Но это означает, что система присвоит службе наивысший приоритет, и поэтому служба не является кандидатом для удаления системой в случае нехватки памяти. Помещать службу на передний план стоит только в том случае, когда это действительно необходимо для создания современного и отзывчивого приложения.
Примеры использования службами переднего плана:
- Приложения, которые проигрывают медиа-файлы в фоне.
- Приложения, которые обновляют данные о местоположении в фоне.
Когда запущенная служба помещается на передний план, она должна вывести на экран уведомление, явно сообщая пользователю, что служба работает. Это важно, потому что запущенная служба на переднем плане отделена от жизненного цикла UI-компонентов (за исключением, разумеется, самого постоянного уведомления). И нет другого способа сообщить пользователю о том, что на его телефоне что-то работает (и потенциально потребляет много ресурсов) кроме как вывести в UI постоянное уведомление.
Ниже пример старта запущенной службы на переднем плане:
Вот код создания постоянного уведомления в версиях
Кроме того, вот еще одна статья, в которой больше деталей о создании уведомлений в MediaStyle (поскольку для фонового проигрывания аудио-файлов нужны как уведомления, так и привязанные и запущенные службы)
Остановка запущенных служб
Обратите внимание, что параметр piStopService типа PendingIntent (который передается в конструктор уведомления) фактически передает Intent с константой Command.STOP типа Integer . Помните, что startService(Intent) может вызываться несколько раз? Это пример такого поведения. Чтобы остановить службу мы запускаем Intent через startService(Intent) и далее обрабатываем этот Intent в методе onStartCommand() запущенной службы.
Это объясняет почему метод onStartCommand() должен уметь обрабатывать Intent ы. Используя этот механизм мы можем «сказать» службе, чтобы она остановила работу. Ниже код, который иллюстрирует эти возможности:
Если вы хотите завершить выполнение запущенной службы на переднем плане, вы должны вызвать stopForeground(true) . Этот метод также завершит работу постоянного уведомления. Однако, саму службу это не остановит. Для этого следует вызвать stopSelf() .
Чтобы остановить службу вы можете выполнить одно из следующих действий:
- Как было показано выше, передайте Intent с дополнительным параметром в startService() , который затем будет обработан в onStartCommand() и фактически служба вызовет stopSelf() . И, если к службе не привязаны никакие другие компоненты, это вызовет onDestroy() и служба завершит свою работу.
- Вы также можете создать явный Intent (указывая на класс службы) и передать его в метод stopService() , который вызовет stopSelf() и, затем, onDestroy() аналогично п.1.
Вот несколько примеров остановки службы из Activity :
И вот код в вашей службе, который будет обрабатывать эти запросы (при условии, что ваша запущенная служба находится на переднем плане):
Привязанные службы
В отличие от запущенных служб, привязанные службы позволяют установить соединение между компонентом Android, привязанным к службе, и службой. Это соединение предоставляется реализацией интерфейса IBinder , который определяет методы для взаимодействия со службой. Простым примером этого может быть реализация привязанной службы в одном процессе с клиентом (т.е. в рамках вашего собственного приложения). В этом случае Java-объект, подкласс Binder , передается клиенту, который может использовать его для вызова методов службы.
В более сложных сценариях, когда необходимо, чтобы интерфейс службы был доступен для разных процессов, для предоставления клиенту интерфейса службы следует воспользоваться объектом Messenger (является ссылкой на объект Handler , который получает обратный вызов для каждого вызова от клиента), благодаря чему со службой можно взаимодействовать с помощью объектов Message . Объект Messenger фактически основан на AIDL (Android Interface Definition Language). Messenger создает очередь из всех запросов клиентов в рамках одного потока, поэтому служба одновременно получает только один запрос. Если необходимо, чтобы служба обрабатывала одновременно сразу несколько запросов, можно использовать AIDL напрямую.
Отличия между привязанной и запущенной службами:
- У клиентского компонента нет соединения с запущенной службой. Он просто использует объекты Intent посредством startService() или stopService() , которые обрабатываются службой в методе onStartCommand() .
- Когда клиентский компонент ( Activity , Fragment или другая служба) соединяются с привязанной службой, они получают реализацию IBinder , посредством которой они могут вызывать методы у привязанной службы.
В любом случае, когда службе (привязанной или запущенной) необходимо отправлять сообщения привязанному клиенту, ей следует использовать что-то вроде LocalBroadcastManager (в том случае, если клиент и служба работают в одном процессе). Привязанные службы обычно не подключаются к привязанному клиентскому компоненту напрямую.
bindService() и onCreate()
Для того, чтобы клиентский компонент стал привязанным к службе, необходимо вызвать bindService() с явным Intent , как и в случае с запущенной службой.
BIND_AUTO_CREATE это наиболее часто встречающийся флаг в случае вызова bindService() . Существуют и другие флаги (например, BIND_DEBUG_UNBIND или BIND_NOT_FOREGROUND ). В случае BIND_AUTO_CREATE у привязанной службы вызывается onCreate() , если служба до этого еще не была создана. Фактически это означает, что служба создается в момент первой привязки к ней.
Как только вызывается bindService() , службе необходимо реагировать на запрос клиента и предоставить ему экземпляр IBinder , посредством которого клиент сможет вызывать методы привязанной службы. В примере выше, это реализуется с помощью ссылки mServiceConnection . Это обратный вызов (callback) ServiceConnection , который привязанная служба будет использовать для уведомления клиента о завершении привязки. Он также позволит клиенту узнать о разрыве соединения со службой.
Другими словами, привязка выполняется асинхронно. bindService() возвращается сразу же и не возвращает клиенту объект IBinder . Для получения объекта IBinder клиенту необходимо создать экземпляр ServiceConnection и передать его в метод bindService() . Интерфейс ServiceConnection включает метод обратного вызова, который система использует для того, чтобы выдать объект IBinder .
Ниже приведен пример реализации ServiceConnection :
Привязка службы
Давайте посмотрим, что происходит на стороне привязанной службы, когда клиент вызывает bindService(Intent) .
В привязанной службе вы должны реализовать метод onBind() , для получения клиентом экземпляра IBinder . Метод ‘onBind()’ будет вызван только один раз, при первой привязке клиента. Для последующих клиентов, система выдаст такой же экземпляр IBinder :
Объект IBinder обеспечивает программный интерфейс, с помощью которого клиенты могут взаимодействовать со службой. Как говорилось выше, самый простой способ реализации IBinder — это расширение класса Binder , экземпляр которого возвращается из метода onBind() :
В примере выше, мы просто используем метод getService() , который просто возвращает Java-объект привязанной службы клиентскому компоненту. Ссылаясь на этот экземпляр IBinder , клиент может вызывать публичные методы у привязанной службы напрямую. Обратите внимание, что эти методы выполняются в клиентском потоке. И в случае Activity или Fragment эти методы будут выполняться в главном потоке. Поэтому стоит быть осторожным с методами в привязанной службе, которые могут блокировать поток или могут стать причиной ANR.
Отвязка от службы и вызов onDestroy()
Чтобы отвязаться от привязанной службы, клиент просто вызывает unbindService(mServiceConnection) . Затем система вызовет onUnbind() в самой службе. И, если у привязанной службы больше нет клиентов, и также, если, служба не является запущенной службой, то система вызывает onDestroy .
Вот как выглядит вызов unbindService() в клиентском компоненте:
В коде выше, метод onStop() в Activity переопределен для вызова unbindService() . В зависимости от требований UX к приложению ваш клиентский компонент может привязываться к службе и отвязываться от нее в методах onStart() и onStop() соответственно, или в любых других методах жизненного цикла клиентских компонентов на ваше усмотрение.
Вот пример как может выглядеть onUnbind() в коде привязанной службы:
Обычно вы вернете false . Но, если вернуть true , то при привязке следующего клиента к службе вместо onBind() будет вызван метод onRebind() .
Привязанные и запущенные службы одновременно
Бывают ситуации, когда вам могут пригодиться службы, которые являются запущенными и вместе с тем могут допускать привязку. В предыдущих разделах, мы показали особенности работы каждого из видов служб. И уже из этих особенностей можно понять, что создание привязанных и запущенных служб одновременно необходимо для реализации особого поведения в момент начала работы со службой и при завершении работы с ней.
Если служба не запущена, то клиент, который хочет привязаться к ней, вызовет onCreate() у службы. Если служба уже запущена, этот метод не вызывается. С другой стороны, если клиент отвязывается от службы и при этом служба не запущенная, то вызывается onDestroy() и служба уничтожается.
Вы можете запустить службу путем вызова метода startService(), вывести ее на передний план и показывать постоянное уведомление. Таким образом, вы реализуете все, что мы говорили о создании запущенных служб. Но кроме того, вы можете реализовать методы, которые позволят клиентам привязываться к службе, с помощью вызова метода bindService() . Особенность такой »двойной» службы в том, что даже при отвязке всех клиентов, служба продолжает свою работу и выполняется до тех пор, пока сама не остановит себя с помощью метода stopSelf() , или до тех пор, пока другой компонент не вызовет метод stopService() .
Переход в запущенное состояние
Поскольку клиент, привязываясь к службе, не переведет ее в запущенное состояние, то для привязанных и запущенных служб одновременно, требуется чтобы служба переходила в запущенное состояние самостоятельно. Вот, как можно это сделать с учетом Android O:
В коде под спойлером:
- Метод commandStart() может быть вызван клиентом, который привязывается к службе.
- Или commandStart() вызывается через методы startService() или startForegroundService() (для Android O).
Но, перед фактическим исполнением работы, служба сначала переводит себя в запущенное состояние.
Итак, когда клиент привязывается к службе, вызывается commandStart() . Служба еще не запущена. Давайте посмотрим на код, и увидим, что случится:
- Если служба привязывается к клиенту, то она не запущена (и в mServiceStarted содержится false )
- В этом случае вызывается moveToStarted() и там создается явный Intent с Extras Command.START , и далее вызывается startService() или startForegroundService() .
- Это приводит к вызову onStartCommand() , который опять вызывает commandStart() .
- Но теперь в commandStart() значение переменной mServiceIsStarted равняется true , и поэтому метод commandStart() выполняет свое прямое предназначение, т.е. запускает полезную работу службы.
Завершение работы службы и отвязывание
Если служба не в запущенном состоянии и клиентский компонент отвязывается от службы, то служба уничтожается и вызывается onDestroy()
Но если она в запущенном состоянии она не уничтожается. И она будет «убита», только если запущенное состояние будет остановлено (через вызов stopService(Intent) или вызов startService(Intent) c Extras в Intent , которые говорят о намерении остановить службу, например Command.STOP ).
Вот диаграмма, в которой суммируются состояния службы и переходы между ними для запущенной и привязанной службы одновременно:
Примеры
Реализацию большинства из того, о чем говорилось в статье, можно глянуть на GitHub.
Это небольшая утилита для Android O и N, которая держит телефон в активном состоянии, если он на зарядке.
Источник