Зачем это нужно?
Казалось бы, есть таймеры по расписанию, есть фоновый сервис, асинхрон, всякие асинхронные кнопки – зачем еще один вид фоновой обработки? Тут есть несколько причин, разделим их на две категории: «чего мы можем лишиться» и «что мы можем приобрести».
Итак, чего мы можем лишиться, если не будем использовать WorkManager:
- Андроид борется за жизнь батарейки и просто может прибить наш фоновый процесс, если он ему покажется подозрительно долгим. Таким образом товары не передадутся в 1С, или что-нибудь другое не выполнится
- Пользователь может переключиться на другое приложение, Simple останется в фоне, а там недалеко и до сна, и судьба фоновой задачи становится под вопросом
- Во время старта задачи, например, плохая связь и задача не может быть выполнена
- Гугл периодически проводит ротацию технологий, и «неправильные» технологии сначала становятся deprecated, а потом и запрещенными, а WorkManager считается сейчас актуальной
- Что-то запланированное по расписанию не выполнится, опять же в связи с оптимизацией
Что же мы приобретаем:
- Задачи выполнятся даже если приложение неактивно или выключено
- Даже после ПЕРЕЗАГРУЗКИ УСТРОЙСТВА, без срока давности
- Мы можем назначить условия выполнения задачи, например, когда устройство находится на зарядке или когда есть интернет
- Периодические задачи будут выполняться по расписанию несмотря ни на что
Другими словами, оформляя воркер, мы передаем Андроиду задание, и дальнейшая его судьба уже лежит в зоне ответственности ОС. Нет нужды контролировать исполнение – это делает операционная система. Также операционная система контролирует условия выполнения этого задания, например наличие Интернет-соединения. Т.е. если такое условие есть, задание не будет запущено, если нет связи, и как только связь появится, система запустит выполнение задачи. При этом Simple живет своей жизнью.
Как это реализовано в 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 – менеджер. То есть, другими словами, некоторые команды не выполнятся, если приложение вообще не запущено или находится в сне. Но есть команды, которые будут выполнены даже если девайс перезапущен и в памяти не висит SimpleUI. Например, работа с СУБД через нативный обработчик – без проблем, HTTP-запросы – тоже без проблем и даже, как ни странно, online обработчики тоже не зависят от контекста. Например, в демо кнофигурации к этому релизу есть воркер с таким массивом обработчиков:
workercode=[{"action":"run","type":"http","method":"GET #long1c/get_goods"},
{"action":"run","type":"set","method":"SQLParameter=@ResultMessage"},
{"action":"run","type":"sql","method":"insert into goods(art,barcode,nom) values(?,?,?)"},
{"action":"run","type":"set","method":"speak=Данные загружены"}]
Вот он нормально отрабатывает после перезагрузки устройства, правда команду speak он не выполнит, если при этом приложение не болтается в памяти, но тем не менее данные с веб-сервиса дергает и записывает в таблицу goods. Проверено.
"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, тому есть причины:
- Самое главное – эти задачи передают и скачивают файлы в бинарном виде в режиме multipart, т.е. поддерживают докачку и постоянное соединение, что критично для больших файлов и слабых каналов. Даже для небольших файлов (фотографий), при наличии слабого канала есть смысл использовать данный тип фонового задания, тем более это очень просто.
- Соответственно раз есть Boundary и цикл побайтового чтения, то в этот цикл вписывается прогрессбар в шторке уведомлений, что позволяет контролировать прогресс выполнения
- Упрощенный вызов для таких задач: если вам надо что-то оправить или скачать, достаточно указать настройки подключения и имя файла, остальное сделается автоматически, сразу можно определить postExecute на событие после загрузки
Для скачивания:
StartDownloadWorkRequest,{"request":<описание запроса>,"tag":<тег задачи>,"title":<необязательный, заголовок в шторке уведомлений>,"body":<необязательный, текст в уведомлении>}
Описание запроса: {"url":<URL или псевдоним точки доступа>,"method":<метод HTTP>,"file":<имя файла, куда будет производиться запись>,"postExecute":<при необходимости, массив обработчиков по окончанию выполнения> }. Если используется альяс, предварительно записанный в HTTPAddAlias, то как правило, в нем есть все необходимое для подключения – авторизация, заголовки. Если не используется то можно определить сразу в описании запроса.
Для отправки:
StartUploadWorkRequest,{"request":<описание запроса>,"tag":<тег задачи>,"title":<необязательный, заголовок в шторке уведомлений>,"body":<необязательный, текст в уведомлении>}
Описание запроса: {"url":<URL или псевдоним точки доступа>,"method":<метод HTTP>,"file":<имя файла, куда будет производиться запись>,"postExecute":<при необходимости, массив обработчиков по окончанию выполнения> }
Телега проекта, в которой масса всего полезного: https://t.me/devsimpleui