- Бровин Ярослав
- Блог одного из разработчиков FireMonkey
- Приложение будильник. Использование AlarmManager в FireMonkey на Андроиде
- Описание проблемы
- Решение в виде AlarmManager
- Шаг 0. Пара слов об использовании java в нативных приложения Delphi
- Шаг 1. Создание BroadcastReceiver
- Шаг 2. Регистрация AlarmReceiver
- Шаг 3. Формирование интента
- Шаг 4/5. Запрашиваем сервис AlarmManager и отправляем нашу задачу
- Программируем будильник или использование AlarmManager в Android
- Несколько слов о классе AlarmManager
- Программируем приложение-будильник для Android
Бровин Ярослав
Блог одного из разработчиков FireMonkey
Приложение будильник. Использование AlarmManager в FireMonkey на Андроиде
В этой статье вы узнаете, как создать будильник, построенный на системном планировщике задач AlarmManager Андроида в FireMonkey. AlarmManager позволяет поставить на выполнение задачу, которую требуется выполнить в определенный момент времени. Главным отличием от обычного таймера является то, что задание будет выполнено не зависимо от того, живо ли ваше приложение или нет.
Система автоматически по наступлению указанного времени, выполнит ваш код. О том, как это работает и как этим пользоваться будет описано в этой статье.
Описание проблемы
Представьте, что вам требуется сделать приложение будильник, которое должно в определенное время подать звуковой сигнал и разбудить пользователя. Мы могли бы использовать компонент TTimer для этого и по наступлению определенного времени подать звуковой сигнал:
Но, как только приложение будет закрыто, ваш будильник не сработает. Такая реализация требует, чтобы приложение постоянно находилось в памяти и постоянно работало. Как вы понимаете в реальности на мобильных устройствах такой подход не допустим и приложение может быть, как выгружено системой в целях экономии ресурсов, так и приостановлено. Я уж не говорю о практичности такого будильника, который запрещает вам работать с другими приложениями на устройстве.
Что нам требуется, чтобы будильник сработал даже, когда приложение закрыто?
Нам нужно, чтобы кто-нибудь подал нам сигнал, когда придет время просыпаться. По этому сигналу мы могли бы запустить наше приложение и выполнить наш код. Например, проиграть звонок. Оказывается, Андроид уже предусмотрел для этих целей специальных механизм. Он называется AlarmManager.
Решение в виде AlarmManager
AlarmManager — это специальный системный сервис, позволяющий выполнить пользовательский код в определенный момент времени. Этот менеджер является частью системы Андроид, постоянно находится в памяти и бдит за временем и задачами. Как только приходит положенное время, он извлекает помещенную в него заранее задачу и инициирует выполнение задачи.
Типичный алгоритм работы с сервисом такой:
- Создаем класс «задачи». Наследник от BroadcastReceiver (об этом в этой статье позже), который предоставляет специальный абстрактный метод OnReceive, который будет вызван сервисом AlarmManager в указанный момент времени. В метод OnReceive через параметр сервис AlarmManager передаст наше заранее заготовленное сообщение с нашими параметрами.
- Регистрируем наш ресивер в нашем приложении. Говорим системе, что у нас есть такой ресивер.
- Формируете сообщение с нашими параметрами. Сообщение — это отложенное намерение PendingIntent (далее в статье).
- Запрашиваете у системы сервис AlarmManager
- Отправляете задачу, указывая: время/интервал выполнения задачи, отложенный интент (2 пункт).
Что такое интент?
Интент (Intent) — это дословно намерение на выполнение какого-то действия. По сути, интент представляет собой аналог сообщения или посылки в реальной жизни. Сообщение, имеющее:
- Адрес получателя — если получатель один.
- Категорию адресатов — если получателей несколько
- Набор данных, который мы хотим послать адресатам.
Интенты бывают двух видов:
- Intent — Мгновенные, которые отправляются сразу и выполняются сразу
- PendingIntent — Отложенные, которые складываются в очереди и отправляются уже в будущем.
В этой статье нас интересуют только отложенные уведомления. Так как мы хотим разбудить человека в определенный момент времени в будущем, а не прямо сейчас.
Шаг 0. Пара слов об использовании java в нативных приложения Delphi
Перед тем, как мы рассмотрим все шаги подробнее. Я хочу обратить ваше внимание на понимание процесса, как используются любые java классы в Delphi. Именно понимание этого, облегчит вам процесс интеграции ваших java классов в ваше нативное приложение.
Ваше Delphi приложение с точки зрения исполняемого кода состоит из двух главных частей:
- Нативная so библиотека с вашим кодом на языке Delphi
- Исполняемый код вашего приложения — classes.dex. Именно этот файл содержит стартовое активити вашего приложения. И именно этот файл загружает нативную so библиотеку с вашим кодом. Именно этот файл содержит java реализацию дополнительных классов, требующих для работы FireMonkey.
Если вы хотите добавить ваш собственный java класс, его нужно добавить в classes.dex файл. Это делается при помощи специальных утилит идущих в поставке с Android SDK.
Dex файл — это файл с инструкциями для исполняемой java машины Dalwik. Именно он и запускается на андроиде и выполняется на исполняемой машине Dalwik. Поскольку нам не доступны исходники из которых этот файл был построен, мы можем только добавить в этот файл свои классы.
Общий алгоритм добавления своих java классов такой:
- Создаем файлы с java классами.
- Компилируем java классы java компилятором javac и получаем class файлы
- Пакуем ваши class файлы в jar файл (архив со специальной внутренней структурой, манифестом и тд)
- Получаем dex файл из jar
- Смешиваем полученный dex файл c dex файлом Embarcadero. (Результирующий файл содержит старые классы и ваши новые.)
- Заменяем dex файл embarcadero новым через Deployment Manager.
Теперь ваши классы будут в вашем приложении. Остается только понять, как их вызывать и использовать из нативного кода. Для этого нужно получить «хедера» для них при помощи утилиты java2op, которая идет в поставке RAD Studio. Натравив ее на ваш jar файл, утилита выдаст pas файл с классами для работы с вашими java классами.
Теперь перейдем непосредственно к созданию BroadcastReceiver.
Шаг 1. Создание BroadcastReceiver
BroadcastReceiver — это абстрактный java, класс выступающий в качестве получателя интентов. Другими словами — он является тем получателем посылок, которые мы будем ему отправлять. Именно здесь мы и будем будить пользователя звонком будильника.
Теперь нам требуется сделать наследника этого класса и написать код воспроизведения мелодии. Сделать в Delphi мы это не можем, так как JNI не позволяет наследовать java классы в нативном коде.
JNI — это специальная библиотека Java виртуальной машины для использования java их нативных языков C++, Delphi и тд.
Поэтому используя java создаем нашего наследника com.test.AlarmReceiver. Создаем в обычном текстовом редакторе текстовый файл AlarmReceiver.java, перекрываем метод OnReceive и выполняем проигрывание стандартной мелодии будильника. Для этого используем RingtoneManager.
Этот файл обязательно помещаем в каталоги: «com\test«. Так как пакет этого класса назван com.test.
Теперь необходимо этот файл скомпилировать и добавить в наше приложение, чтобы мы могли его использовать (смотрите шаг 0). Наверное, это наиболее сложная часть — внедрение вашего класса в уже готовый файл от Embarcadero.
Воспользуемся bat файлом для сборки файла (я позаимствовал его у Андрея Ефимов). Я взял за основу батник и немного модифицировал его.
Вам требуется указать правильные пути в своем окружении:
- ANDROID — путь к sdk адроида
- ANDROID_PLATFORM — путь к версии андроида.
- DX_LIB — путь к инструментам для получения и соединения dx файлов
- EMBO_DEX — путь к местоположению эмбаркадеровского classes.dex файлы
После того, как вы вызовите эту утилиту, в папке «output\dex» будет находиться новый classes.dex файл. Теперь нужно заменить им classes.dex файл по умолчанию. Для этого открываем Deployment в RAD Studio (Menu -> Project -> Deployment) и добавляем новый файл. Важно, что путь назначения (Remote Path) для этого файла должен быть «classes\«.
Теперь, нужно получить delphi хедеры для нашего AlertReceiver java класса. Для этого пользуемся утилитой java2Op (утилита располагается в папке bin или bin\converterers\java2op в каталоге с Rad Studio):
на выходе получаем хедер:
Шаг 2. Регистрация AlarmReceiver
Процесс регистрации прост. Нужно всего лишь добавить в манифест приложения эту информацию. Открываем в текстовом редакторе файл AndroidManifest.template.xml, расположенный в папке с исходниками вашего приложения. И добавляем строчку
После . Таким образом мы сказали операционной системе, что у нас есть ресивер AlarmManager и он находится в пакете com.test.
Шаг 3. Формирование интента
Интент формируется просто:
В 5 строчке делаем сообщение, и в 6 строчке указываем, что класс, который должен принять этот сообщение — это наш AlarmReceiver. В положенное время:
- Сервис системы посмотрит на сообщение
- Извлечет название класса
- Создаст класс
- Передаст туда это сообщение в метод OnReceive
В 7 строчке мы создаем отложенное сообщение. Указывая контекст — приложения в рамках которого будет наше сообщение. AID — уникальный ID для возможности отменить задание в будущем. FLAG_UPDATE_CURRENT — указывает, что задание нужно обновить, если такое уже было в системе.
Шаг 4/5. Запрашиваем сервис AlarmManager и отправляем нашу задачу
Создаем интент и посылаем его через метод set.
- RTC_WAKEUP — указывает, что когда время выполнения задания придет, нужно разбудить устройство, если оно находится в спящем состоянии.
- Второй параметр — время, когда требуется выполнить задание.
Код метода конвертирующего TDateTime во время андроида.
Теперь можно запустить приложение. Выберите время, когда вы хотите проснуться и и нажмите кнопку поставить будильник.
Источник
Программируем будильник или использование AlarmManager в Android
При разработке приложения порой бывает необходимо выполнять какие-то действия в будущем. Например, вы хотите создать будильник или периодически отправлять данные на свой сайт в строго определенное время. Для решения подобных задач в Android используется классAlarmManager, который позволяет выполнять код в требуемый момент времени, даже если ваше приложение не запущено. То-есть AlarmManager — аналог corn в Linux или планировщика задач Windows. Вы говорите, что такой-то кусок кода должен выполниться тогда-то и AlarmManager обеспечивает запуск кода. В англоязычный литературе таймер, который выполняет код в требуемый момент времени обозначается терминомalarm. Давайте в рамках данной статьи называть этот таймербудильником, чтобы отделить это понятие от таймера.
Несколько слов о классе AlarmManager
Фактически класс AlarmManager обеспечивает доступ к сервису планировки задач Android. Для получения объекта этого класса нужно вызвать методContext.getSystemService(Context.ALARM_SERVICE).AlarmManagerрегистрирует в системе интент и когда наступает обозначенное время, AlarmManager запускает этот интент. Если момент вызова приложение закрыто, то оно будет вновь запущено. AlarmManager нужно использовать только в случае, если код должен быть выполнен даже при закрытом приложении, во всех других случаях рекомендуется использовать классHandler.
Класс AlarmManager довольно прост. В нашем распоряжении шесть методов:
- void cancel(PendingIntent operation) — отменяет установленный таймер для переданного в качестве параметра интента.
- void set (int type, long triggerAtMillis, PendingIntent operation) — устанавливает будильник, который сработает один раз.
- void setInexactRepeating (int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) — установка будильника с неточным повторением.
- setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)— установка будильника с точным повторением.
- setTime(long millis) — установка системного времени.
- setTimeZone(String timeZone) — установка временной зоны, выбранной по-умолчанию.
ЗдесьPendingIntent— класс, который представляет интент и связанное с его выполнением действие. Передавая PendingIntent другому приложению Вы тем самым даете ему право запускать кусок кода из своей программы.
В качестве типа будильника (параметрtype) может быть передано одно из значений ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC и RTC_WAKEUP. ELAPSED_REALTIME и ELAPSED_REALTIME_WAKEUP — время задается с момента загрузки устройства, а RTC и RTC_WAKEUP — системное время в UTC. Для ELAPSED_REALTIME и RTC если устройство находится в спящем режиме в момент срабатывания будильника, то связанный с ним интент будет вызван позже, когда пользователь выведет устройство из спящего режима. Для ELAPSED_REALTIME_WAKEUP и RTC_WAKEUP если устройство находится в спящем режиме, оно немедленно пробуждается и вызывается интент.
Программируем приложение-будильник для Android
Давайте в качестве примера разработаем приложение — будильник. При запуске программы пользователю будет показано окно с тремя кнопками. С их помощью пользователь сможет: установить будильник, который срабатывает один раз; будильник, который будет срабатывать периодически; а также удалить ранее установленные будильники.
Создадим новый проект в макет шаблона добавим три кнопки
КнопкаbtStartустанавливает повторяющийся будильник, кнопкаbtCancel— отменяет его. КнопкаbtOneTime— создает не повторяющийся будильник. Прямо в шаблоне мы указали методы, которые будут вызываться при нажатии на кнопки: startRepeatingTimer, cancelRepeatingTimer и onetimeTimer соответственно. Код этих методов будут приведен в классе Activity.
Для работы сAlarmManagerнапишем отдельный класс. В качестве базового класса используем BroadcastReciever. Наш класс будет управлять зарегистрированным с помощью AlarmManager интентом. Мы переопределим методonReceive(), который будет вызываться после получения интента. Внутри метод onReceive() мы должны попытаться получить связанные с интентом параметры. В своей программе мы будем использовать один параметр ONE_TIME, который позволяет определить, относится ли интент к однократно срабатывающему будильнику или нет. После того, как значение ONE_TIME получено, пользователю показывается соответствующее сообщение.
Также в этом классе определим методы setAlarm(), cancelAlarm() и onetimeTimer(). Конечно эти методы можно было определить и в другом месте, мы включили их в данный класс из соображения простоты примера.
- Метод setAlarm() устанавливает повторяющийся будильник с помощью метода setRepeating(). Этому методу требуется четыре параметра: тип будильника, время запуска (устанавливаем текущий момент), интервал в миллисекундах, интент, который будет вызываться при срабатывании будильника.
- Метод cancelAlarm() отменяет зарегистрированный ранее будильник с помощью вызова метода cancel(), которому перезается в качестве параметра интент. При совпадении этого параметра с зарегистрированным ранее интентом, произойдет удаление будильника.
- Метод onetimeTimer() создает будильник, который срабатывает один раз. Делается с помощью метода set(), которому передается три параметра: тип будильника, время запуска, вызываемый интент.
Ниже приводится файл манифеста. Обратите внимание, для корректной работы программе необходимо дать разрешениеWAKE_LOCK, поскольку мы используем блокировку потока в методе onReceive(). Также тут мы регистрируем AlarmManagerBroadcastReceiver, как получатель широковещательных сообщений
Теперь давайте напишем класс, в котором реализуем обработчики кнопок. Здесь мы создадим экземпляр описанного выше класса AlarmManagerBroadcastReciever и будем вызывать методы setAlarm(), cancelAlarm() и setOnetime().
При надатии на кнопки будут установлен будильник. Исходный код проекта можно скачатьздесь.
Источник