Запуск из экранов
Асинхронность на экранах предполагает, что после того, как будет вызвана асинхронная процедура, UI-поток приложения не блокируется, вы можете работать дальше как обычно, а долгая процедура выполняется в фоне. У нее может быть что-то по окончании, что должно как-то оповестить пользователя, коллбек вызов, а может не быть.
В общем случае, для того чтобы вызвать любой обработчик (онлайн, Пайтон и т.д.), достаточно просто установить действие runasync вместо run. Для таких обработчиков можно определить обработчик, который будет вызван по окончании выполнения асинхронного обработочика в параметре postExecute (а у того в свою очередь может быть свой postExecute и т.д. до бесконечности, но так обычно не бывает).
Соответственно и интерфейс не блокируется, и обработчики, если таковые есть на событии, следующие за асинхронным вызовом, выполняются сразу, а не по завершении долгого обработчика. Это надо иметь в виду. Если вам надо, чтобы выполнилось именно по завершении – то postExecute.
Нас интересует, что произойдет в интерфейсе по окончании выполнения асинхронного обработчика. Интерфейсные действия (команды) могут быть отображаемые на экранах и не отображаемые (голос, звук, тосты, уведомления в шторку и т.п.). Для вторых не нужен открытый экран, они могут отработать в фоне спокойно, лишь бы был контекст самого приложения. Соответственно с ними и проблем меньше – если вы вызываете, например, команду vibrate, то она отработает, если приложение не в глубоком сне в любом случае, то же самое с остальными.
Но иногда по окончании асинхронного вызова надо обновить инфу на экране. Для этого есть команда RefreshScreen. Допустим, вы делаете долгий запрос, который по окончании алгоритма выводит результат в переменную, а переменная есть на форме экрана. Переменная-то установится, но без RefreshScreen пользователь это не увидит.
Что касается места вызова UI-команды, то это неважно. Это можно сделать в postExecute, но необязательно. Если даже не использовать postExecute вообще, а просто в асинхронном обработчике вывести переменную и вызвать RefreshScreen, то информация на экране обновится.
Важно для понимания: RefreshScreen не вызывает обработчики onStart, она просто перерисовывает экран по-быстрому.
Запуск из кода
Допустим, у вас код, внутри которого вы хотите запустить один или несколько асинхронных обработчиков. Лучший способ – это точно такой же запуск runasync из предыдущего раздела, только в предыдущем разделе подписка на событие определяется в конфигураторе, а если надо из кода, то можно запустить вручную командой RunEvent.
Исторически есть также команда RunPyThreadDef, которая просто запускает асинхронно питоновскую функцию в поток без возможности определения коллбека, но в описанном ранее способе, во-первых, есть коллбек, во-вторых, можно запускать любой интерпретатор, а не только Python, ну и в-третьих это универсальный подход для всех ситуаций.
Запуск runprogress
Запуск прогресс-бара тоже предназначен для долгих алгоритмов, но в другом качестве. При запуске прогресс-бара как раз интерфейс блокируется (прогрессбаром). Таким образом пользователь видит не просто повисание системы, а прогрессбар. Для этого нужно просто определить действие runprogress, в который можно обернуть любой обработчик. Например, если у вас решение – онлайн и связь тормозит, можно поставить узкие моменты в ruprogress.
Какой вариант лучше для пользователя, runasync или runprogress, зависит от того, как устроен ваш интерфейс. Если пользователь, переключившись на экран, уже может что-то вводить, а некая долгая, но второстепенная инфа подоспеет позже – тогда runasync самое то.
В последнем релизе 11.50.42 – по окончании runprogress можно указать postExecute. Дело в том, что для системы runprogress асинхронный (несмотря на то, что выглядит это не так) и обработчики, следующие за runprogress, выполняются сразу. Если надо что-то выполнить именно по окончании – то postExecute в ноом релизе это позволит сделать.
ActiveCV и runasync
Для ActiveCV тоже бывает нужна асинхронность, и работает она примерно так же. В демо конфе к этому примеру при запуске CV-процесса запускается «долгий» обработчик, после чего приложение помещает штрихкод в зеленый список. Это позволяет запустить сканирование без задержки, а потом уже выводится штрихкод. Никаких RefreshScreen тут нет, достаточно просто поместить переменную в postExecute долгого обработчика (так же, как и без postExecute). Это работает с любыми типами обработчиков.
Надо сказать, что кроме onStart другие события не прерывают видеопоток и отрабатывают по умолчанию в фоне, поэтому можно для остальных событий запускать просто run
Общие обработчики системы (не Фоновый сервис)
У платформы есть общие обработчики неких событий, которые происходят не на экране, а вообще в приложении. Например в Simple есть встроенный веб-сервер, который может принимать запросы извне. Отправив на него POST-запрос http://<адрес, порт устройстве>?mode=SyncCommand, можно вызвать и обработать событие onWebServiceSyncCommand . При этом в теле запроса можно в JSON передать все, что надо, а в ответ получить либо стек переменных, либо свой какой нужно ответ.
Например, в демо базе обработчик ожидает от запроса JSON либо {"command":"speak"} либо {"command":"listen"}.
При этом очевидно, что в момент появления такого события на экране может быть все, что угодно – экран процесса, меню или что-либо другое. Определить, что же в данный момент запущено из любого обработчика, можно по переменным current_operation_name и current_screen_name. Это может понадобиться, чтобы скорректировать поведение обработчика в зависимости от того, какой экран в данный момент запущен
Аналогично другим обработчикам если экран открыт – то установка переменной+RefreshScreen выведут на экран то, что нужно. Не-экранные элементы (тосты, звуки и т.д.) отработают в любом случае, если приложение открыто.
Фоновый сервис
Фоновый сервис – это сервис, запускающийся вместе с SimpleUI (если в конфигурации стоит такая опция) и работающий независимо от приложения. Можно сказать, что это 2 разных приложения, более того, сервис может остаться работающим в системе, если приложение выгружено из памяти.
Через сервис по умолчанию проходит прием сообщений через подписки на интенты, в т.ч. штрих-коды в случае настройки подписки на интент, Bluetooth – штрихкоды, распознавание речи. Также на сервис можно отправить свою процедуру (BackgroundCommand). В дальнейшем на сервисе также будут регистрироваться и другие события (WebSocket и другие внешние события).
Таким образом, это надежный инструмент исполнения, висящий всегда в памяти и готовый принять событие.
Аналогично другим обработчикам он может взаимодействовать с экранами (через RefreshScreen)
А также, например, запускать любой процесс и экран ShowProcessScreen или диалог. Т.е., другими словами, сервис может получить событие внешнее или внутреннее и, если есть на чем вывести, выведет на экране. Если нет – может открыть экран, вывести диалог или отправить уведомление, звук, синтез речи.
В демо базе к этой статье содержится такой пример: через веб сервер подается запрос {"command":"listen"}, после чего в сервис событий подается команда на распознавание речи. По результатам этой команды идет показ результата распознавания в открытом экране. А если он не открыт – открывает.
Воркеры
Подробно воркеры разобраны в статье //infostart.ru/1c/articles/1825700/. В двух словах воркеры позволяют запускать долгие задачи (или периодические задачи) с гарантированным выполнением – система будет пытаться выполнить это, пока у нее не получится. Каждая такая задача передается ОС на выполнение и Андроид уже сам следит, когда ее запускать. В плоскости применения симпла это как правило задачи синхронизации – фотографий, товаров, документов и т.д. Особенно по плохим каналам связи.
В рамках этой статьи нас интересует, как воркер сможет взаимодействовать с интерфейсом по мере своего выполнения. Коротко так:
• Если есть контекст приложения и открыт экран (либо можно открыть экран), то, как и все другие фоновые процессы, может вывести инфу на экран, используя RefreshScreen. В демо примере как раз такой пример.
• Если в памяти еще есть контекст приложения, но само приложение закрыто, заблочен экран – то может выполнить контекстные функции (тост, синтез речи, уведомление и т.д.). И сам Python, кстати, тоже требует контекста
• Если приложение вообще выгружено из памяти либо находится в глубоком сне, воркер контекстные функции выполнить не может (в т.ч. обработчики на Python), но при этом может выполнить нативные функции и онлайн функции. Вот такой парадокс – если вы запустили воркер, перезагрузили устройство и после перезагрузки он выполнился (приложение не запущено), то в 1С он будет выполнять обработчики, пистаь в СУБД будет, отправлять запросы тоже, но все, что касается контекста – нет.
Таймеры(периодические задания)
В целом про взаимодействие с интерфейсом из таймеров можно сказать все то же самое, что и с вышеперечисленными обработчиками, с одной важной оговоркой – если таймер выполняется по новому – т.е. ссылается на Общие обработчики через alias (для этого в таймере в Имя обработчика надо указать alias через @, например @test_alias, а в общем обработчике надо указать этот alias – test_alias). Под одним alias может запускаться несколько строк Общих обработчиков
В целом есть отличие Таймеров от периодического воркера. Периодический воркер работает независимо от приложения (из-за этого иногда приходится заморачиваться с тем, чтобы его загасить), а таймеры работают, пока приложение хоть как-то активно.
Телеграмм-канал проекта, в котором масса всего полезного: https://t.me/devsimpleui
Видео, как все работает: