- Коммуникация между Activity и Service
- Пример
- Реализация
- Заключение
- Организация архитектуры взаимодействия Activity и Service
- С высоты птичьего полета
- Сервис
- ServiceHelper
- Activity
- Заключение
- Как сделать так, чтобы Android-сервис общался с Activity
- Creating a Background Service in Android Using IntentService
- 2. IntentService Limitations
- 3. Why do we need IntentService?
- Case Study
- 4. Create an IntentService
- How it works
- 5. Declaring Service in the Manifest
- 6. Sending Work Requests to the IntentService
- 7. Report Status From IntentService to Activity
- 8. Receive Status Broadcasts from an IntentService
- DownloadResultReceiver.java
- MainActivity.java
- 9. Output
- 11. Download Source Code
Коммуникация между Activity и Service
Нам нужно передавать данные из активити в сервис и обратно. Как нам это сделать? Для решения нашей задачи у нас уже есть все необходимое. Все что нужно — это привязать сервис к ативити, используя bindService, передать нужные параметры и немного магии в виде использования классов Message. А магия заключается в том, чтобы использовать переменные экземпляра Message и в частности, replyTo. Данная переменная нужна нам, чтобы мы могли обратиться к экземпляру Messanger сервиса из активити и в сервисе к экземпляру Messanger-а активити. На самом деле, не так уж и просто. По крайней мере для моего не самого одаренного ума. Отчасти, я улучшаю документацию, которая уже есть — Services. Улучшаю тем, что добавляю связь с активити, передавая данные туда-обратно, чего нет в документации. Также, есть хороший пример на StackOverflow. В любом случае, надеюсь статья будет полезна хоть кому-то и я потрудился не зря.
Пример
В качестве примера реализуем сервис, который будем увеличивать и уменьшать значение счетчика и возвращать результат в активити, в TextView. Код макета опущу, ибо там две кнопки и текстовое поле — все просто.
Реализация
Приведу полностью код активити:
Поясню. При создании активити мы сразу привязываемся к сервису, реализуя интерфейс ServiceConnection и в нем оправляем сообщение сервису «установить значение счетчика», передавая ноль и создавая toServiceMessanger, передавая в конструктор интерфейс IBinder. Кстати, в сервисе обязательно нужно вернуть этот экемпляр, иначе будет NPE. С помощью этого класса мы и отправляем сообщения сервису. И вот она магия — в переменную replyTo мы сохраняем наш другой экземпляр Messenger — тот который получает ответ от сервера и именно через него и будет осуществляться связь с активити.
Для получения сообщения от сервиса используем свой Handler и просто ищем нужные нам переменные и делаем по ним действия. По кликам на кнопки(методы countIncrClick, countDecrClick) отправляем запросы к сервису, указывая нужное действие в переменной msg.what.
Далее, полный код сервиса:
Все по аналогии с логикой в активити. Даже не знаю, нужно ли что-то пояснять. Единственный момент — это то, что я сразу отправляю запрос обратно в активити в handleMessage, используя для этого волшебную переменную replyTo и вытаскивая выше нужный Messenger. И второй момент о котором я уже говорил — это:
без которого все упадет. Именно данный экземпляр интерфейса и будет передан в ServiceConnection
Заключение
Вцелом все. Такой вот надуманный пример взаимодействия активити и сервиса. Мне кажется, довольно таки нетривиальное взаимодействие, хотя кому-то может показаться иначе.
Код проекта есть на Bitbucket
Вопросы, уточнения и прочее в личку. Могут быть неточности по поводу каких-либо аспектов, поэтому смело пишите и поправляйте.
Надеюсь, пост был полезен хабраюзерам.
Источник
Организация архитектуры взаимодействия Activity и Service
Сегодня я решил поведать Вам мой способ организации activity-service interaction в Android приложениях. Мотивирован топик тем, что достаточно часто можно встретить приложения, в которых, скажем, поход на сервер организовывается внутри активити в AsyncTask. При этом часто встречается верная мысль, что это надо делать в сервисах, но нигде в оф. документации нет ни слова об организации правильной архитектуры двустороннего взаимодействия между ними.
Поэтому я методом проб и ошибок пришел к архитектуре, лично для меня покрывающей все необходимые вопросы.
Об этом методе я буду рассказывать далее.
С высоты птичьего полета
Давайте сначала рассмотрим высокоуровневую картину предлагаемой архитектуры.
Далее в статье я буду использовать два термина — управляемая и неуправляемая обратная связь. Это неофициальные термины, но я их буду использовать, т. к. они мне нравятся. Управляемые — это уведомления, осуществляемые платформой Android для нас (ContentProvider + ContentObserver система). Для того, чтобы UI получал управляемые уведомления нам ничего не нужно, кроме корректно реализованного провайдера.
Гораздо интересней — как реализованы неуправляемые уведомления, т. е. те, которые осуществляются при помощи нашей системы событий. Ведь не всегда выполнение какой-то операции в сервисе сопряжено с записью в провайдер, поэтому нам нужен свой механизм уведомления клиента о том, что сервис завершил работу.
Итак, данная архитектура подразумевает наличие четырех основных компонентов системы:
- Activity, выполняющую стандартную роль отображения интерфейса
- Service — сервис, выполняющий тяжелую работу в background потоке
- ServiceHelper — наш компонент, который будет склеивать нашу активити и сервис и предоставлять неуправляемые уведомления
- ContentProvider — необязательный, в зависимости от вашего UI компонент, который будет помогать осуществлять управляемые уведомления.
Сервис
Наш сервис выполняет роль command processor’а.
Каждый входящий интент несет в extras:
- Действие, которое необходимо выполнить
- Аргументы, определяемые командой
- ResultReceiver
Сервис смотрит на переданный action, сопоставляет ему команду, которую нужно выполнить, и передает аргументы и ResultReceiver команде.
Самый простой вариант реализации сервиса:
Здесь в большом блоке if просто ищется нужная команда. Понятное дело, здесь можно как угодно загнаться, чтобы избежать ифа: держать Map action-handler, сделать фабрику, использовать IoC и т. п., но это выходит за рамки статьи.
Handler
Обработчики инкапсулируют в себе выполняемую процедуру. У меня они образуют определенную иерархию, где базовый класс выглядит как:
следующим уровнем иерархии я реализовал базовую команду, выполняющую подготовку http запроса, но это, опять же выходит за рамки статьи. В целом, Вы наследуетесь от базовой команды и реализуете doExecute, в котором при необходимости вызываете sendUpdate метод, передаете код (успех/ошибка) и Bundle с данными.
ServiceHelper
ServiceHelper — это промежуточный слой между UI и сервисом, упрощающий вызовы к сервису для UI, и выполняющий рутинные операции по упаковке интентов. Также он координирует координирует ответы от сервиса и содержит информацию о командах, выполняющихся в данный момент.
Итак, как это работает:
- UI вызывает метод хелпера, хелпер возвращает ID запроса
- Хелпер запоминает ID запроса
- Собирает Intent, в который вкладывет ResultReceiver и отправляет сервису
- когда сервис завершает операцию, в onReceiveResult оповещаются все слушающие UI компоненты
Давайте посмотрим на код:
Сервис хелпер держит список подписчиков в массиве, именно на этот список будут рассылаться уведомления по работе команд.
это — общий метод по созданию нашего интента, который мы зашлем сервису.
Более интересным местом является pendingActivities — это регистр всех выполняющихся на данный момент задач на сервисе. Поскольку при вызове метода ServiceHelper мы получаем id, мы всегда можем узнать, выполняется команда или нет. Подробней об этом — чуть далее в статье.
Теперь пример public метода, который будет выполнять какое-то действие на нашем сервисе:
и вот таких методов, торчащих наружу будет ровно столько, сколько команд поддерживает ваш сервис.
Activity
Итак, как я уже сказал, у нас есть интерфейс:
Я считаю, что удобно в базовой абстрактной активити реализовывать сей интерфейс. Тогда в конкретных активити Вам надо будет всего лишь переопределить метод onServiceCallback для получения уведомлений, что очень похоже на стандартные callback методы в activity, т. е. грациозно вписывается в Ваш клиентский код.
Обратите внимание, как активити подписывается и отписывается от ServiceHelper в своих методах onResume/onPause. Это позволяет избегать проблем при пересоздании активити, например, при повороте экрана, или сворачивании приложения.
Давайте рассмотрим, что нам приходит в метод onServiceCallback:
- requestId — уникальный идентификатор, сгенерированный при отправке запроса
- requestIntent — оригинальный интент, который мы послали
- resultCode — код результата выполнения
- resultData — данные
Теперь, у нас есть все необходимое, чтобы в нашей activity мы всегда могли получить уведомление от нашего сервиса без кучи boilerplate кода.
Более того, что мне кажется очень полезным — мы можем идентифицировать как конкретный запрос по ID, так и все запросы одного типа по action, что дает нам огромную гибкость.
Также, имея ID запроса, мы можем выполнять отложенную проверку. Представим последовательность:
- пользователь запустил действие, запустилась крутилка
- закрыл приложение на 2 минуты
- действие уже выполнилось
- пользователь открыл снова
- тут мы проверяем в onResume, выполнилась ли операция, и убираем крутилку
т. е., просто вызываем getServiceHelper().isPending(requestId), если нам это нужно.
Заключение
Вот, пожалуй, и все.
Сразу скажу, что я не претендую на универсальность данной архитектуры или на какую-то ее уникальность. Она была медленно выведена мной путем проб и ошибок, просмотров различных материалов и т. п. Но, пока, все мои нужды в настоящих коммерческих проектах она покрывает на 100%.
Более того, если чего-от не хватает — ее можно легко расширить. Из очевидного:
- добавить помимо success и failure код progress, тогда с сервиса можно будет передавать информацию о прогрессе задачи и отображать ее в, скажем ProgressBar
- прикрутить код по прерыванию выполняемой задачи
- и т. п.
Еще одна деталь, у меня код ServiceHelper не синхронизирован, т. к. подразумевается, что его методы будут вызываться в UI thread всегда. Если у Вас это не так, то необходимо добавить синхронизацию при любом изменении состояния ServiceHelper.
В общем, спасибо за внимание, надеюсь, кому-то поможет. Готов отвечать на Ваши вопросы и замечания в комментах.
UPD: Выложил маленький sandbox примерчик, иллюстрирующий архитектуру на GitHub: https://github.com/TheHiddenDuck/android-service-arch
UPD 2: Добавил пример реализации сервиса, работающего на пуле потоков
Источник
Как сделать так, чтобы Android-сервис общался с Activity
Я пишу свое первое приложение для Android и пытаюсь наладить связь между услугами и действиями. У меня есть служба, которая будет работать в фоновом режиме и делать некоторые записи GPS и времени. У меня будет Активность, которая будет использоваться для запуска и остановки Сервиса.
Итак, во-первых, я должен быть в состоянии выяснить, работает ли Служба при запуске Действия. Здесь есть и другие вопросы, так что я думаю, что смогу это выяснить (но не стесняйтесь предлагать советы).
Моя настоящая проблема: если активность запущена, а служба запущена, мне нужен способ для отправки сообщений в активность. Простые строки и целые числа на данный момент — сообщения о статусе в основном. Сообщения не будут появляться регулярно, поэтому я не думаю, что опрос службы — это хороший способ, если есть другой путь. Я хочу получить это сообщение только тогда, когда действие было запущено пользователем — я не хочу запускать действие из службы. Другими словами, если вы запустите Activity, а служба будет запущена, вы увидите некоторые сообщения о состоянии в пользовательском интерфейсе Activity, когда произойдет что-то интересное. Если вы не запустите действие, вы не увидите эти сообщения (они не так интересны).
Похоже, я должен быть в состоянии определить, запущена ли служба, и если да, добавить активность в качестве прослушивателя. Затем удалите Activity как слушателя, когда Activity приостановится или остановится. Это действительно возможно? Единственный способ понять, как это сделать, — это заставить Activity реализовать Parcelable и создать файл AIDL, чтобы я мог передать его через удаленный интерфейс Сервиса. Хотя это кажется излишним, и я понятия не имею, как Activity должна реализовывать writeToParcel () / readFromParcel ().
Есть ли более простой или лучший способ? Спасибо за любую помощь.
РЕДАКТИРОВАТЬ:
Для тех, кто заинтересован в этом позже, есть пример кода от Google для обработки этого через AIDL в каталоге примеров: /apis/app/RemoteService.java
Существует три очевидных способа общения с сервисами:
- Использование Intents
- Использование AIDL
- Использование самого сервисного объекта (как синглтона)
В вашем случае я бы выбрал вариант 3. Сделать статическую ссылку на сервис, который он сам, и заполнить его в onCreate ():
Сделайте статическую функцию MyService getInstance() , которая возвращает статическую sInstance .
Затем при Activity.onCreate() запуске службы асинхронно дождитесь, пока служба фактически не запустится (ваша служба может уведомить ваше приложение о готовности, отправив намерение в действие.), И получить его экземпляр. Когда у вас есть экземпляр, зарегистрируйте свой объект прослушивателя службы для вашей службы, и вы настроены. ПРИМЕЧАНИЕ: при редактировании представлений внутри Activity вы должны изменить их в потоке пользовательского интерфейса, сервис, вероятно, будет запускать свой собственный поток, поэтому вам нужно вызвать Activity.runOnUiThread() .
Последнее, что вам нужно сделать, — это удалить ссылку на ваш объект слушателя Activity.onPause() , иначе экземпляр вашего контекста активности будет неэффективным.
ПРИМЕЧАНИЕ. Этот метод полезен только в том случае, если ваше приложение / действие / задача является единственным процессом, который будет обращаться к вашему сервису. Если это не так, вы должны использовать вариант 1. или 2.
Источник
Creating a Background Service in Android Using IntentService
IntentService is a subclass of android.app.Service class. A stated intent service allows to handle long running tasks without effecting the application UI thread. This is not bound to any activity so, it is not getting effected for any change in activity lifecycle. Once IntentService is started, it handles each Intent using a worker thread and stops itself when it runs out of work.
IntentService would be an best solution, If you have an work queue to process. For example, if your application using analytics you will likely to send event name and related parameter to your tracking server for each user generated event. Although each event means a tiny piece of data, creating networking request on each click will result an overhead to your application. Instead, you can use work queue processor design pattern and process the events in a batch.
2. IntentService Limitations
- No easy or direct way to interact with user interface directly from IntentService. Later in this example, we will explain to pass result back from IntentService to
- With IntentService, there can only be one request processed at any single point of time. If you request for another task, then the new job will wait until the previous one is completed. This means that IntentService process the request
- An tasks stated using IntentService cannot be interrupted
3. Why do we need IntentService?
Android design guidelines strongly suggests to perform all the long running tasks off the UI thread. For example, if you have to periodically download the largest chunk of data from server, you must use IntentService to avoid ANR. ANR (Application not responding) message often occurs, if your main thread is doing too much of work. In this course of this tutorial, we will learn the below concepts
- How to create and use IntentService
- How to pass data from activity to service as parameter
- How to pass result back to activity
- Update activity based on the result
Case Study
To make this tutorial easy to understand we will extend our previous tutorial (Android Networking Tutorial) to use Intent Service for downloading the data from server. We suggest you to checkout Android Networking Example to get familiar with downloading data from server using different http clients available in Android.
Expected Result Start service to download the data when application is started. Once download is complete, update ListView present in your activity.
Feed Response Object
4. Create an IntentService
In the context of our example, we will create an IntentService to download the data from server. Once download is completed, the response will be sent back to activity. Lets create a new class DownloadService.java and extend it from android.app.IntentService . Now let us override onHandleIntent() method.
When service is started the onHandleIntent() method is called on the worker thread.Unlike Service, IntentService stops itself once it completes its task, so you don’t need to call stopSelf() for stoping the IntentService.
How it works
- DownloadService class extending IntentService and overriding onHandleIntent() method. In onHandleIntent() method we will perform our network request to download data from server
- Before it downloads the data from server, the request is being fetched from bundle. Our Activity will send this data as extras while starting the
- Once Download is successful we will send the response back to activity via ResultReceiver
- For any exceptions or error, we will pass the error response back to activity via ResultReceiver.
- We have declared custom exception class DownloadException for handling all our custom error messages. You may do this
5. Declaring Service in the Manifest
Like Service , an IntentService also needs an entry in your application manifest. Provide the element entry and declare all your IntentServices you using. Additionally as we are performing operation to download data from internet, we will request for android.permission.INTERNET permission.
6. Sending Work Requests to the IntentService
To start the DownloadService to download data, you must create an explicit Intent and add all the request parameters to it. A service can be started by calling startService() method. You can start an IntentService either form an Activity or a Fragment .
What is the additional DownloadResultReceiver here, huh?. Remember that we have to pass the result of download request from service to activity. This will be done through ResultReceiver .
7. Report Status From IntentService to Activity
To send the status of a work request in an IntentService to other components, get the instance of ResultReceiver. Send the status by calling send() method.
8. Receive Status Broadcasts from an IntentService
To receive results back from IntentService, we can use subclass of ResultReciever . Once results are sent from Service the onReceiveResult() method will be called. Your activity handles this response and fetches the results from the Bundle. Once results are recieved, accordingly the activity instance updates the UI.
DownloadResultReceiver.java
MainActivity.java
9. Output
11. Download Source Code
Download complete example source code from GitHub
Источник