- Android studio service timer
- Выполнение задач в бэкграунде
- Почему может «тормозить» Service
- Что делать?
- Запускаем свой поток
- AsyncTask — все проще
- Таймер. Самый простой подход к периодическому запуску.
- Более гибкий способ. ScheduledThreadPoolExecutor.
- Задание со звездочкой. Велосипед. Thread, Looper, Handler.
- Полный список
- Таймер с ручным запуском
Android studio service timer
Для того, чтобы в фоновом режиме выполнить отчет времени, по истечению которого в вашем приложении должно произойти какое либо событие, можно использовать встроенный класс Timer и TimerTask. Сам класс Timer служит исключительно для отсчета времени, он может работать в двух режимах: одноразовое выполнение задания, либо же повторяющееся действие (например, напоминание, что у вас не лицензионная версия антивируса :). Метод TimerTask используется для описания действий, которые должны происходить по истечению времени отсчета в таймере Timer. То есть, здесь реализуется визуальное проявление работы таймера, иначе он просто будет считать себе в фоне и все без толку, никто о нем даже не узнает.
Мы сделаем сначала совсем простую вещь — создадим приложение с таймером, которое по истечению времени в одну секунду будет высвечивать пользователю текущее время на аппарате, а потом попробуем усложнить задачу и выполнять с помощью таймера запуск второго activity.
Создаем новый проект, выбираем Blank Activity. В файле activity_main.xml создаем интерфейс нашего приложения:
Теперь вся остальная работа будет происходить с помощью кода в MainActivity.java. Здесь мы выполняем стандартные шаги: объявляем используемые объекты, связываемся с элементами интерфейса. Ну и задаем Timer и метод для выполнения задачи по истечению таймера TimerTask:
Вот так выполняется реализация системного Android Timer. Пробуем запустить и смотрим на результат:
Теперь сделаем немного сложнее — при истечении 5 секунд в таймере будем запускать второе окно приложения. Как создать второе окно и выполнять на него переход мы уже детально разбирали раньше.
Как вы уже поняли, все изменения будут касаться лишь времени таймера:
и описания действий в методе MyTimerTask :
Запускаем и тестируем:
Переход выполнен успешно! Как видите, Android Timer довольно простая и интересная вещь, теоретически в метод TimerTask можно запихнуть что угодно. Поэкспериментируйте! А еще освойте счетчик обратного отсчета CountDownTimer.
Источник
Выполнение задач в бэкграунде
На Stackoverflow часто встречаются вопросы по выполнению на Android фоновых задач, в т.ч. и повторяющихся с заданным промежутком времени. Как правило, первое, что используется, это Service.
Такой подход в некоторых случаях может привести к тормозам и низкой скорости ответа пользовательского интерфейса. Расскажу когда так бывает и как с этим бороться…
Почему может «тормозить» Service
Каждое Android-приложение по-умолчанию запускается в отдельном процессе. В каждом процессе запускаются потоки (Thread). По-умолчанию все компоненты приложения (Activity, Service, BroadcastReceiver) запускаются в одном «main» потоке (он же UI-thread). Если внутри сервиса запустить, например, долгий сетевой вызов или какую-то тяжелую инициализацию, мы получим тормоза всего приложения, его интерфейса и, скорее всего, предложение сделать Force close… Впрочем, работа службы в том же потоке, что и остальное приложение, имеет свои плюсы — у вас есть доступ к элементам интерфейса. Если бы служба работала в другом потоке, доступ к UI у вас бы отсутствовал.
Что делать?
Для решения данной проблемы стоит для тяжелых задач использовать отдельный поток. На самом деле его даже не обязательно создавать внутри службы…
Для запуска задачи в отдельном потоке можно воспользоваться следующими средствами SDK:
- создать Thread
- использовать AsyncTask
Запускаем свой поток
Поток создать просто…
Все просто, но проблемы начинаются, когда после выполнения длинного задания нам захочется обновить UI.
В результате выполнения получим ошибку. «Чужой» поток попытался обратиться к UI! Как вылечить? Надо использовать Handler. Доработаем код…
AsyncTask — все проще
Для реализации подобных задач в Android SDK имеет встроенное средство — AsyncTask. Данный класс позволяет не думать о том, в каком потоке выполняется ваш код, все происходит автоматически. Рассмотрим пример выше переписанный на AsyncTask.
Метод doInBackground будет выполнен в отдельном потоке, результат его выполнения будет передан в метод onPostExecute, который, в свою очередь будет выполнен на UI-Thread’е
Следует помнить, что:
- AsyncTask может выполняться лишь раз. Для повторного запуска нужно пересоздать класс;
- execute() должен быть выполнен на UI-Thread’е.
А что делать, если задачу нужно выполнять регулярно, через определенные промежутки времени…
Таймер. Самый простой подход к периодическому запуску.
Java предоставляет Timer для запуска повторяющихся задач. Сделаем так, чтобы AsyncTask из предыдущего примера выполнялся раз в минуту…
Стоит заметить, что все упрощается если «долгоиграющая» задача не требует доступ к UI. В этом случае не требуются ни Handler’ы, ни AsyncTask’и.
Кстати, у таймера есть еще метод scheduleAtFixedRate(). Различия между ним и schedule() описаны в документации.
Более гибкий способ. ScheduledThreadPoolExecutor.
Класс ScheduledThreadPoolExecutor указан как рекомендуемая альтернатива использованию Timer. Данный класс позволяет организовать пул потоков и планировать выполняемые задачи относительно него. Т.е. класс для организации очереди заданий один, но тем не менее он может выполнять одновременно несколько заданий, если это позволяет имеющееся количество доступных потоков. Более подробно о преимуществах — в документации.
Для каждого заплаированного задания доступен его «дескриптор» — ScheduledFuture с помощью которого можно, например, отменить выполнения одного конкретного задания не трогая весь остальной пул.
Задание со звездочкой. Велосипед. Thread, Looper, Handler.
А еще можно собрать свой велосипед поток с очередью.
Данный поток, будучи единожды запущенным, выполняется вечно. Для общения с ним (отправки заданий) можно использовать метод getHandler() для получения хандлера и дальнейшей отправкой «событий» в него. CountdownLatch используется для синхронизации, чтобы поток, желающий получить Handler, не получил его ранее того момента, когда поток-работник запустится и Handler буде создан.
Источник
Полный список
— обмен данными в биндинге
Подключаться к сервису мы теперь умеем. В этом уроке попробуем обменяться с ним данными.
В сервисе метод onBind возвращает объект, наследующий интерфейс IBinder. Проще всего использовать для этого объект Binder и расширить его необходимыми нам методами. Т.е. создаем свой класс MyBinder с предком Binder и рисуем в нем свои методы.
При биндинге, в методе onServiceConnected мы получаем объект Binder. Мы можем привести его к типу MyBinder (из сервиса) и вызывать его методы, а реализация будет срабатывать в сервисе, где мы описывали этот класс.
Как вы понимаете, это сработает только, если сервис и приложение выполняются в одном процессе. Поэтому такой биндинг называется локальным.
Нарисуем пример. У нас будет сервис, который с определенным интервалом будет что-то выводить в лог. Мы к нему подключимся, и будем повышать или понижать интервал и получать новое значение интервала обратно.
Project name: P0981_ServiceBindingLocal
Build Target: Android 2.3.3
Application name: ServiceBindingLocal
Package name: ru.startandroid.develop.p0981servicebindinglocal
Create Activity: MainActivity
Добавим в strings.xml строки:
Кнопки для запуска сервиса, повышения интервала и понижения интервала.
Создаем сервис MyService.java:
Здесь мы используем таймер – Timer. Он позволяет повторять какое-либо действие через заданный промежуток времени. Кратко распишу принцип действия. TimerTask – это задача, которую Timer будет периодически выполнять. В методе run – кодим действия этой задачи. И далее для объекта Timer вызываем метод schedule, в который передаем задачу TimerTask, время через которое начнется выполнение, и период повтора. Чтобы отменить выполнение задачи, необходимо вызвать метод cancel для TimerTask. Отмененную задачу нельзя больше запланировать, и если снова надо ее включить – необходимо создать новый экземпляр TimerTask и скормить его таймеру.
Итак, в методе onCreate мы создаем таймер и выполняем метод schedule, в котором стартует задача.
Метод schedule проверяет, что задача уже создана и отменяет ее. Далее планирует новую, с отложенным на 1000 мс запуском и периодом = interval. Т.е. можно сказать, что этот метод перезапускает задачу с использованием текущего интервала повтора (interval), а если задача еще не создана, то создает ее. Сама задача просто выводит в лог текст run. Если interval = 0, то ничего не делаем.
Метод upInterval получает на вход значение, увеличивает interval на это значение и перезапускает задачу. Соответственно задача после этого будет повторяться реже.
Метод downInterval получает на вход значение, уменьшает interval на это значение (но так, чтоб не меньше 0) и перезапускает задачу. Соответственно задача после этого будет повторяться чаще.
onBind возвращает binder. Это объект класса MyBinder.
MyBinder расширяет стандартный Binder, мы добавляем в него один метод getService. Этот метод возвращает наш сервис MyService.
Т.е. в подключаемом Activity, в методе onServiceConnected мы получим объект, который идет на выход метода onBind. Далее преобразуем его к типу MyBinder, вызовем getService и вуаля — у нас в Activity будет ссылка на объект-сервис MyService.
В onCreate создаем Intent для доступа к сервису и ServiceConnection. В методе onServiceConnected мы берем binder, преобразуем его к MyService.MyBinder, вызываем метод getService и получаем наш сервис MyService. Теперь мы можем выполнять методы сервиса. Нас интересуют методы увеличения и понижения интервала.
В методах onStart и onStop мы соответственно подключаемся к сервису и отключаемся.
В onClickStart запускаем сервис.
В onClickUp и onClickDown проверяем, что соединение с сервисом есть, и вызываем соответствующие методы сервиса для увеличения или понижения интервала. В качестве дельты изменения интервала передаем 500. В ответ эти методы передают нам новое значение интервала, и мы выводим его в TextView.
Все сохраним и запускаем.
Жмем Start – запускаем сервис. В логах:
MyService onCreate
MyService onBind
MainActivity onServiceConnected
Создается сервис, и мы к нему подключаемся. Биндинг был ранее вызван в onStart и, когда сервис стартанул, мы автоматически подключились. В прошлом уроке мы разбирали такую ситуацию, когда биндинг идет к незапущенному пока сервису.
Далее с интервалом в одну секунду в лог падает текст run.
Попробуем увеличить интервал. Жмем Up.
На экране появилось текущее значение интервала. И по времени записей в логах видим, что именно с этим интервалом срабатывает задача (вывод текста run в лог) .
Далее можно нажимать Up и Down и наблюдать, как меняется интервал выполнения задачи. Таким образом, мы из Activity подключились к сервису и управляем его работой.
В начале урока я отметил, что все это будет работать, если приложение и сервис в одном процессе. Если же они в разных процессах, то используется немного более сложный вариант для обмена данными. Но это гораздо менее используемый случай, поэтому сейчас я про него не рассказываю. Но попозже об этом обязательно будет статья.
На следующем уроке:
— шлем уведомление из сервиса
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Таймер с ручным запуском
Недавно мне нужно было сделать таймер, запускающий некую задачу, через определенный промежуток времени.
Но отличия от стандартного решения заключались, в следующем:
- Во-первых, если задача не смогла выполниться, тут же необходимо повторить её. И так, до победного конца. После этого, возвращаемся в штатный режим.
- Во-вторых, может возникнуть необходимость запустить задачу вручную, не дожидаясь наступления следующего срабатывания таймера.
- И в-третьих, таймер должен запуститься в том случае, если устройство перезагружено, или была восстановлена связь с интернетом.
Задача была решена следующим образом.
Напишем тестовое приложение.Создайте проект, с пустой активностью.
Создадим новый IntentService, с именем UniversalService.
В MainActivity внесем изменения в onCreate:
И добавим метод:
В данном коде (в методе onCreate), мы сохраняем в Общие Настройки, с именем «AlertTest», состояние чекбоксов, и (в методе setServiceAlarm) запускаем таймер с периодичностью 5 секунд, на выполнение службы UniversalService.
Также, мы хотим получать ответ от службы, об успешности выполнения операции.
Для этого добавим в MainActivity класс:
И в конец onCreate добавим:
А в UniversalService поменяем код на следующий:
Как видите, метод verify, эмулирует нашу задачу. В том случае, если она выполнена неуспешно — создается Intent, запускающий MyBroadRec, который опять стартует нашу службу. Настроить мы можем через чекбокс «Очень важная опция». Если мы переведем чекбокс «Автоматический запуск» в неактивное состояние, то работа таймера будет прекращена. Как только задача будет выполнена — переходим в штатный режим.
Но у нас еще осталась задача автозапуска нашего приложения. Для этого создаем класс UniversalReceiver наследующий от BroadcastReceiver:
И в манифесте добавляем следующие строки:
Все, задача решена. Засим позвольте откланяться. Надеюсь кому-то это окажется полезным. Если у кого-то есть замечания и подсказки, с большим удовольствием их выслушаю.
Источник