Фоновая работа и таймеры

Обработчики runasync и runprogress из экрана

Для того чтобы выполнить предполагаемо долгий процесс на экране либо в общем обработчике достаточно в массиве обработчиков указать действие runasync либо runprogress. runasync – это выполнение в фоновом потоке (AsyncTask), не блокирующее интерфейс. runprogress запускает визуальный прогресс-бар чем блокирует интерфейс. Надо понимать, что runprogress блокирует интерфейс логически (для пользователя) а не вызывает зависания. Для ОС Андроид есть разница, выполняется ли ваш долгий обработчик в режиме run и вызывает фриз экрана или в режиме runprogress. В первом случае это может вызвать ошибку ANR.

По окончанию выполнения любого обработчика возникает событие postExecute, в котором можно определить массив обработчиков на коллбэк по завершению. И так можно образовывать бесконечные цепочки обработчиков на одно событие.

Важно понимать, что если вы запустили при открытии экрана (onStart) обработчик, который что то должен изменить на экране (например заполнение списка), то в этом обработчике обязательно нужно указать команду RefreshScreen

При этом необязательно помещать RefreshScreen в postExecute (хотя можно и так), потому что обработчик анализирует контекст вызова и инициирует выполнение команд на экране, если он открыт.

Важно для понимания: RefreshScreen не вызывает обработчики onStart, она просто перерисовывает экран с новыми переменными. Также можно использовать UpdateLayout, переменная_контейнера для перерисовке только конкретного контейнера

Обновление из фона вне экрана

При выполнении из другого контекста (фоновый сервис, общие обработчики) действует правило – если экран открыт, то сработает команда RefreshScreen этого экрана (и другие «экранные» команды). При этом можно конкретно в стек переменных экрана помещать переменные, даже если вы выполняете в фоновом сервисе (у фонового сервиса свой стек переменных) функциями типа put_process_hashMap(key,value) модуля android.

Если экран не открыт то его можно запустить из фона командой ShowProcessScreen

Таймеры(периодические задания)

Таймеры определяются в разделе конфигурации Таймеры. Нужно определить уникальный ключ, Период (указывается в миллисекундах) и также нужно указать alias – такой же alias нужно определить в Общих обработчиках. Смысл такой: когда срабатывает таймер с определенным alias система ищет в Общих обработчиках такой же alias и выполняет его как любое другое событие (в Общем обработчике не нужно указывать событие и listener).

Таймеры можно запустить/остановить из кода:

  • StartTimers, пустой параметр – инициализирует объект таймеров (необходимо выполнить один раз при запуске) – это запуск объекта, в котором будут запускаться сами таймеры.

  • StartTimer, {«handler»:<массив обработчиков>,»period»:<периодичность, мс>} - добавляет новый таймер и запускает его

  • StopTimers, пустой параметр – останавливает объект таймеров и удаляет все ранее добавленные таймеры (командой StartTimer)

Воркеры

Задачи WorkManager рекомендованы Андроид и являются (периодические воркеры) более предпочтительные чем таймер из предыдущей главы (но с оговорками, так как есть ограничения). Такой подход более щадящий для батареи устройства и гарантирует выполнение задачи при доступности соответствующих условий.

Какие преимущества:

  1. Задачи выполнятся даже если приложение неактивно или выключено

  2. Даже после перезагрузки устройства, без срока давности

  3. Мы можем назначить условия выполнения задачи, например, когда устройство находится на зарядке или когда есть интернет

  4. Периодические задачи будут выполняться по расписанию несмотря на состояние запустившего его приложения

Другими словами, оформляя воркер, мы передаем Андроиду задание, и дальнейшая его судьба уже лежит в зоне ответственности ОС. Нет нужды контролировать исполнение – это делает операционная система. Также операционная система контролирует условия выполнения этого задания, например наличие Интернет-соединения. Т.е. если такое условие есть, задание не будет запущено, если нет связи, и как только связь появится, система запустит выполнение задачи. При этом Simple живет своей жизнью.

Однократные фоновые задания

Для запуска используется команда -переменная StartWork, в качестве параметра которой передается json с обработчиками, которые надо выполнить, и тегом задачи. Тег нужен для того, чтобы в случае чего по нему можно было обратиться и отменить выполнение:

StartWork, {"work":<массив обработчиков>,"tag":<тег задачи>,"retry":<флаг повтора>,"conditions":<список условий>}

Массив обработчиков, при этом – унифицированный массив обработчиков в Архитектуре 2.0, как он задается начиная с 11 релиза. Например, это может быть просто вызов питон-процедуры:

workercode=[{"action":"run","type":"python","method":"some_longtime_routine"}]

Тут есть некоторые нюансы. Выполнение Python и выполнение некоторых команд зависит от контекста приложения, т.е. например, чтобы выполнить ShowScreen, нужно, чтобы было хотя бы в свернутом виде запущено приложение Simple и открыт какой то процесс в нем. Для speak нужен просто контекст приложения, чтобы был запущен TTS – менеджер. То есть, другими словами, некоторые команды не выполнятся, если приложение вообще не запущено или находится в сне.

retry. Установка этого флага заставляет в случае неудачи выполнения (исключение в коде или ошибка http-запроса >200), переключаться в состояние RETRY, что ставит задачу снова в очередь выполнения. Ориентируется на ErrorMessage, который заполняется при ошибках либо может быть вызван искусственно. Например, планируется отправить большой файл с устройства, связь со стороны устройства есть, но запрашиваемый URL недоступен, тогда при этом флаге система будет перепланировать это задание до тех пор, пока оно не будет выполнено или снято вручную

conditions – устанавливает условия запуска задачи. Их может быть одно или несколько сразу (разделитель – «;») Условия доступны следующие:

  • CONNECTED – наличие связи

  • CHARGING – устройство находится на зарядке

  • BATTERY_NOT_LOW – заряд батареи достаточен

  • IDLE – устройство не используется

Периодические задания

Определение задачи такое же, как у однократной, только указывается период. RETRY для периодических не работает.

StartPeriodicWork, {"work":<массив обработчиков>,"period":<период>,"tag":<тэг задачи>,”conditions”:<список условий>}

Запускает периодическую задачу. Период задается в минутах, не менее 15 минут. Если указано менее 15 минут, то будет выполняться раз в 15 минут.

Остановка задач

StopWork, параметр <тег задачи> Останавливает задачу с определенным тегом. Это может быть периодическая задача или однократная в состоянии RETRY

Особые воркеры для скачивания и отправки файлов

Несмотря на то, что описанные выше воркеры могут решать любые задачи, включая отправку файлов, предусмотрено еще 2 команды, которые заточены именно на отправку и получение файлов – StartUploadWorkRequest и StartDownloadWorkRequest, тому есть причины:

  1. Самое главное – эти задачи передают и скачивают файлы в бинарном виде в режиме multipart, т.е. поддерживают докачку и постоянное соединение, что критично для больших файлов и слабых каналов. Даже для небольших файлов (фотографий), при наличии слабого канала есть смысл использовать данный тип фонового задания, тем более это очень просто.

  2. Соответственно раз есть Boundary и цикл побайтового чтения, то в этот цикл вписывается прогрессбар в шторке уведомлений, что позволяет контролировать прогресс выполнения

  3. Упрощенный вызов для таких задач: если вам надо что-то оправить или скачать, достаточно указать настройки подключения и имя файла, остальное сделается автоматически, сразу можно определить postExecute на событие после загрузки

Для скачивания:

StartDownloadWorkRequest,{"request":<описание запроса>,"tag":<тег задачи>,"title":<необязательный, заголовок в шторке уведомлений>,"body":<необязательный, текст в уведомлении>} Описание запроса: {"url":<URL или псевдоним точки доступа>,"method":<метод HTTP>,"file":<имя файла, куда будет производиться запись>,"postExecute":<при необходимости, массив обработчиков по окончанию выполнения> }. Если используется альяс, предварительно записанный в HTTPAddAlias, то как правило, в нем есть все необходимое для подключения – авторизация, заголовки. Если не используется то можно определить сразу в описании запроса.

Для отправки: StartUploadWorkRequest,{"request":<описание запроса>,"tag":<тег задачи>,"title":<необязательный, заголовок в шторке уведомлений>,"body":<необязательный, текст в уведомлении>}

Описание запроса: {"url":<URL или псевдоним точки доступа>,"method":<метод HTTP>,"file":<имя файла, куда будет производиться запись>,"postExecute":<при необходимости, массив обработчиков по окончанию выполнения> }