Пример разработки мобильного приложения на 1С:Підприємство 8.3 для работы курьера интернет-магазина при доставке товаров клиентам. Для разработки использована конфигурация "Сборщик мобильных приложений"

Пример разработки мобильного приложения для курьера интернет-магазина с помощью "Сборщика мобильных приложений"

Итак, мы разработали мобильное приложение для работы курьера интернет-магазина при доставке товаров клиентам. Конечно, оно достаточно схематично и не может охватить всех задач, реально возникающих в процессе работы курьера. Но оно реализует всю ту функциональность, которую мы хотели показать в этой книге.

Теперь, после того как разработка закончена, нам осталось только собрать наше мобильное приложение в один файл и загрузить его на планшет.

Хотя для сборки мы будем использовать специальную конфигурацию Сборщик мобильных приложений , облегчающую процесс сборки, все равно в первый раз сделать это не просто и не быстро. Поэтому нужно запастись терпением и внимательно и аккуратно выполнить описанную ниже последовательность действий.


Где скачать и как установить Сборщик мобильных приложений

Конфигурация Сборщик мобильных приложений поставляется в составе мобильной платформы. В первой главе книги в разделе «Мобильная платформа 1С:Підприємство» мы распаковывали архив с мобильной платформой на компьютер. В этом каталоге есть папка MobileAppMaker с файлом Setup.exe для установки шаблона конфигурации. Запустим этот файл и установим шаблон конфигурации в каталог шаблонов «1С:Підприємство» (рис. 5.1).

Рис. 5.1. Установка шаблона конфигурации «Сборщик мобильных приложений»

Затем добавим новую информационную базу в список информационных баз «1С:Підприємство» и создадим информационную базу из созданного ранее шаблона (рис. 5.2).

Рис. 5.2. Создание информационной базы «Сборщик мобильных приложений» из шаблона

Затем откроем эту базу в конфигураторе и добавим пользователя Администратор со свойствами Аутентификация 1С:Підприємство, ролями Администратор и Пользователь и языком Русский (рис. 5.3).

Рис. 5.3. Создание пользователя «Администратор»

Сохраним конфигурацию, закроем ее и откроем в режиме 1С:Підприємство от имени пользователя Администратор. Сейчас эта база пустая. Мы должны заполнить в ней все необходимые параметры для сборки, которые будут сохраняться и использоваться для дальнейших сборок.

Сначала (если база пустая) на начальной странице приложения открывается общая справочная информация о сборщике мобильных приложений. Перейти к ней также можно из главного меню – Главное меню > Справка > Содержание справки > Сборщик мобильных приложений. Кроме того, из отдельных форм конфигурации отрываются дополнительные страницы справки о сборке мобильных приложений (рис. 5.4).

Рис. 5.4. Справка по конфигурации «Сборщик мобильных приложений»


Настройка параметров прикладного решения

Сначала мы должны настроить параметры сборщика. Для этого из меню Сервис вызовем пункт Настройки параметров приложения. Собирать мобильное приложение для Apple мы сейчас не будем, поэтому соответствующий флажок оставляем пустым.

В форме настроек нам нужно заполнить таблицу Каталоги компонентов на компьютерах, участвующих в процессе сборки, которая будет содержать пути к программным компонентам, необходимым для сборки мобильного приложения. Для этого нажмем кнопку Создать над этой таблицей (рис. 5.5).

Рис. 5.5. Создание записи таблицы «Каталоги компонентов…»

Откроется форма Пути к компонентам. Вызвав справку из этой формы, можно увидеть ссылки для получения программных компонентов и их описание (рис. 5.6).

Рис. 5.6. Справка, описывающая пути к компонентам

Сначала нужно установить Java SDK и в поле JDK указать каталог, в который этот компонент установлен. Java SDK можно получить по адресу: http://www.oracle.com/technetwork/java/javase/downloads/index.html . Рекомендуется скачивать пакет Java Platform (JDK).

На открывшейся странице вверху нужно нажать на кнопку Download (рис. 5.7).

Рис. 5.7. Получение Java SDK

На следующей странице нужно принять лицензионное соглашение (установить отметку Accept License Agreement) и нажать на ссылку с нужным дистрибутивом в колонке Download (для 64-битной Windows – это пакет jdk-8u60-windows-x64.exe), рис. 5.8.

Рис. 5.8. Получение Java SDK

Полученный инсталлятор нужно запустить и установить Java SDK, например, в каталог: C:\Program Files\Java\jdk1.8.0_60 (рис. 5.9).

Рис. 5.9. Установка Java SDK

Затем этот путь нужно указать в поле JDK формы настройки путей к компонентам приложения Сборщик мобильных приложений (рис. 5.10).

Рис. 5.10. Настройка путей к компонентам приложения «Сборщик мобильных приложений»

В следующем поле формы настройки Рабочий каталог и кеш сборщика нужно указать любой каталог на латинице, в котором программа сборки будет размещать служебные файлы. Пользователь, от имени которого будет выполняться сборка мобильных приложений, должен иметь полные права для этого каталога (рис. 5.11).

Рис. 5.11. Настройка путей к компонентам приложения «Сборщик мобильных приложений»

В поле Android SDK укажем путь к каталогу, в котором находится SDK Manager. Android SDK мы устанавливали в главе 1, в разделе «Android SDK» (рис. 5.12).

Рис. 5.12. Настройка путей к компонентам приложения «Сборщик мобильных приложений»

Затем нужно установить Apache ANT и в поле Apache ANT указать каталог, в который этот компонент установлен. Apache ANT необходим для выполнения сборки мобильного приложения для ОС Android. Apache Ant можно получить .

С этой страницы нам нужно скачать архив apache-ant-1.9.6-bin.zip (рис. 5.13).

Рис. 5.13. Получение Apache ANT

Разархивируем этот файл на компьютер и укажем путь к нему в форме настроек путей к компонентам (рис. 5.14).

Рис. 5.14. Настройка путей к компонентам приложения «Сборщик мобильных приложений»

Затем нужно установить систему PuTTY и в поле PuTTY указать каталог, в который этот компонент установлен. PuTTY можно получить .

PuTTY используется в том случае, если выполняется сборка мобильного приложения для Apple. Для сборки мобильных приложений требуются утилиты pscp.exe и plink.exe. На всякий случай скачаем весь установочный пакет putty-0.65-installer.exe (рис. 5.15).

Рис. 5.15. Получение PuTTY

Полученный инсталлятор нужно запустить и установить PuTTY, например, в каталог: C:\Program Files (x86)\PuTTY (рис. 5.16).

Рис. 5.16. Установка PuTTY

Затем укажем путь, полученный при установке PuTTY, в форме настройки путей к компонентам (рис. 5.17).

Рис. 5.17. Настройка путей к компонентам приложения «Сборщик мобильных приложений»

На этом настройка путей к компонентам завершена. Нажмем Записать и закрыть.


Настройка параметров поставщика

Теперь нам нужно настроить параметры поставщика. Для этого вызовем из меню Сервис пункт Редактировать параметры поставщика.

Откроется форма Поставщики, в которой нужно на закладке Общие параметры указать произвольное наименование поставщика, а также задать Префикс идентификатора приложения. Это поле должно заполняться на латинице и начинаться со строки «com». Правила заполнения этого поля можно посмотреть в контекстной справке, открывающейся по кнопке со знаком «?».

Затем нужно отметить, для каких операционных систем выполняется сборка мобильного приложения. В нашем случае установим флажок Для ОС Android.

Для работы с push-уведомлениями через вспомогательный сервис «1С:Підприємство» укажем параметры доступа к сервису. Для этого нажмем кнопку Добавить над таблицей внизу формы поставщика. В открывшемся окне Параметры доступа к вспомогательному сервису «1С:Підприємство» отметим опцию Регистрировать для – выбранного пользователя, выберем пользователя сборщика – Администратор и укажем электронный адрес и пароль, под которым мы ранее регистрировались на сервисе при тестировании работы с push-уведомлениями. Нажмем кнопку Сохранить и закрыть. Кроме того, можно зарегистрироваться на сервисе «1С:Підприємство» непосредственно из этой формы с помощью кнопки Зарегистрироваться в сервисе «1С:Підприємство», если этого еще не было сделано (рис. 5.18).

Рис. 5.18. Настройка параметров поставщика приложения «Сборщик мобильных приложений»

Кроме того, вызвать окно настройки параметров доступа к сервису «1С:Підприємство» можно из меню Сервис, пункт Параметры доступа к сервису «1С:Підприємство».

После этого необходимо на закладке Параметры для ОС Android заполнить группу полей Ключ разработчика. Для этого сначала создадим ключ разработчика, нажав на ссылку Создать ключ разработчика. В открывшейся форме Создание ключа разработчика произвольно заполним поля (для поля Страна нужно указать код России в стандарте ISO – ru) и нажмем кнопку Сформировать ключ (рис. 5.19).

Рис. 5.19. Настройка параметров поставщика приложения «Сборщик мобильных приложений»

После этого поля-параметры ключа разработчика заполнятся автоматически (рис. 5.20).

Рис. 5.20. Настройка параметров поставщика приложения «Сборщик мобильных приложений»

Значение в поле Хеш SHA1 ключа разработчика будет использоваться в дальнейшем для получения ключа работы с картами Google. Это значение необходимо в том случае, если мобильное приложение будет использовать средства геопозиционирования на платформе Android.

На этом настройка параметров поставщика завершена. Нажмем Записать и закрыть.


Загрузка мобильной платформы

Теперь нам нужно загрузить мобильную платформу «1С:Підприємство», под управлением которой будет работать собранное мобильное приложение. Версий мобильной платформы может быть несколько, но они должны быть не ниже версии 8.3.4.

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

Из командной панели приложения откроем справочник Мобильные платформы и нажмем кнопку Создать. После этого появится диалог выбора файла, в котором нужно выбрать файл с архивом мобильной платформы mobile.zip, который мы сохраняли на компьютере при получении мобильной платформы в первой главе книги, в разделе «Мобильная платформа 1С:Підприємство». Выберем его и нажмем кнопку Открыть.

В случае успешной загрузки платформы откроется форма создания элемента справочника Мобильные платформы, в которой поля Версия мобильной платформы и Наименование заполнятся автоматически, и появится флажок Файлы мобильной платформы загружены (рис. 5.21).

Нажмем Записать и закрыть.


Загрузка мобильной конфигурации

еперь нам нужно загрузить разработанную нами мобильную конфигурацию КурьерИнтернетМагазина. Откроем эту конфигурацию в конфигураторе. В палитре свойств конфигурации зададим свойства Поставщик – myfirm и Версия – 1.0.0 (рис. 5.22).

Рис. 5.22. Свойства мобильной конфигурации «КурьерИнтернетМагазина»

Затем выгрузим эту конфигурацию в файл, выполнив команду конфигуратора Конфигурация > Мобильное приложение > Записать в файл...

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

Из командной панели приложения откроем справочник Мобильные конфигурации и нажмем кнопку Создать группу с наименованием нашей конфигурации Курьер Интернет-магазина (рис. 5.23).

Рис. 5.23. Создание группы справочника «Мобильные конфигурации»

Затем в этой группе создадим новый элемент справочника.

После этого появится диалог выбора файла, в котором нужно выбрать файл 1cema.xml, в который мы только что сохраняли нашу мобильную конфигурацию. Выберем его и нажмем кнопку Открыть.

В случае успешной загрузки конфигурации все поля формы будут заполнены автоматически и не должны изменяться вручную. В таблице разрешений будут указаны все разрешения по работе мобильного приложения с мультимедиа, геопозиционированием, уведомлениями и т. п., которые мы установили при его разработке. Кроме того, будут показаны сообщения о невозможности работы с календарями и контактами, разрешения на которые мы не устанавливали (рис. 5.24).


Нажмем Записать и закрыть.


Описание параметров мобильного приложения

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

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

Из командной панели приложения откроем справочник Мобильные приложения и нажмем кнопку Создать группу. В открывшейся форме зададим наименование мобильного приложения Курьер Интернет-магазина.

Поставщик у нас один – Моя фирма. Он заполнится автоматически. А также установится флажок Для ОС Android. Поле Мобильная платформа оставим пустым – при сборке автоматически будет использована самая «свежая» версия платформы.

В поле Идентификатор решения укажем произвольную строку на латинице. Следующее за ним поле заполнится автоматически (рис. 5.25).

Рис. 5.25. Создание группы справочника «Мобильные приложения»

После этого автоматически заполнится поле Параметр получения ключа для работы с картами Google (в это поле подставляется значение параметра Хеш SHA1 ключа разработчика поставщика Моя фирма из формы настройки параметров поставщика, см. рис. 5.20 + строка идентификатора решения) – это нам понадобится для получения ключа для работы с картами Google. Для этого мы должны обратиться к сервису Google и после получения ключа записать его в поле Ключ для работы с картами Google.

Мы рекомендуем приобрести этот курс в комплекте с курсом “Разработка мобильных приложений на 1С 8.3” .

Во втором курсе мы детально разбираем монетизацию мобильных приложений, а также – что Вам нужно заранее учесть при их разработке.

Возможность добавить в корзину второй курс появится в форме для ввода заказа – после того, как Вы нажмете кнопку “Оформить заказ!” .

Гарантия

Мы ведем обучение с 2008 года, уверены в качестве наших курсов и даем на этот курс нашу стандартную 60-дневную гарантию .

Это значит, что если Вы начали заниматься по нашему курсу, но вдруг передумали (или, скажем, не имеете возможности), то у Вас есть 60-дневный срок для принятия решения – и если Вы производите возврат, мы возвращаем 100% оплаты.

Рассрочка платежа

Наши курсы можно оплатить по частям или в рассрочку, в том числе без процентов. При этом доступ к материалам Вы получаете сразу .

Это возможно при оплате от физических лиц на сумму от 3 000 руб. до 150 000 руб.

Все, что Вам нужно сделать – это выбрать способ оплаты “Оплата через Яндекс.Касса”. Далее на сайте платежной системы выбираете “Заплатить по частям”, указываете срок и размер выплат, заполняете небольшую анкету – и через пару минут получаете решение.

Варианты оплаты

Мы принимаем все основные формы платежей.

От физических лиц – оплаты с карт, оплаты электронными деньгами (WebMoney, ЯндексДеньги), оплаты через интернет-банкинг, оплаты через салоны связи и так далее. Возможна также оплата заказа по частям (в рассрочку), в том числе без дополнительных процентов.

Начните оформлять заказ – и на втором шаге Вы сможете выбрать предпочтительный способ оплаты.

От организаций и ИП – безналичная оплата, предоставляются документы на поставку. Вы вводите заказ – и сразу можете распечатать счет на оплату.

Обучение нескольких сотрудников

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

Если компании требуется обучить нескольких сотрудников, мы обычно предлагаем “дополнительные комплекты”, которые стоят на 40% дешевле.

Для оформления заказа на “дополнительный комплект” выберите в форме 2 и более комплектов курса , начиная с второго комплекта стоимость курса будет на 40% дешевле .

Есть три условия использования дополнительных комплектов:

  • нельзя приобрести только дополнительный комплект, если до этого (или вместе с ним) не был приобретен хотя бы один обычный
  • на дополнительные комплекты не действуют еще какие-то скидки (они и так дисконтированны, получилась бы “скидка на скидку”)
  • на дополнительные комплекты не действуют акции (например, компенсация в 7000 рублей) по той же причине

Я помню то замечательное время, когда сборка релизной версии мобильного приложения сводилась к тому, что нужно было выставить debug = false и запустить экспорт apk-файла. Проходит 2 минуты, пока пыхтит IDE, и все готово. Все усилия сосредотачивались на необходимости указать данные сертификата подписи. Это было совсем недавно. Cейчас процесс сборки того самого приложения разросся настолько, что, если мне, вдруг, потребуется выполнить все операции самостоятельно, и даже если я все вспомню и проделаю безошибочно (во что я не верю), то это займет не час, который сегодня кажется непозволительно долгим, а, скорее всего, сутки, после чего терапевт обязан будет прописать мне больничный по усталости недели на две.

Итак, процесс сборки мобильного приложения. Попробую рассказать, из чего он у нас состоит - не потому, что в последнее время стало модным катать посты о CI той или иной мобильной команды (с покером, русалками и прочими обязательными атрибутами), а потому, что это отличный опыт, который я получил, работая над Почтой Mail.Ru для Android, и потому, что этой возможности, вероятнее всего, не было бы, работай я в другой команде, над другим проектом или в другой компании.

Для любого процесса важным решением является выбор системы, на базе которой будет строиться вся сборка. Билдами должен заниматься билд-сервер. Это логично. Но какой выбрать?

Вопрос неоднозначный, каждый выбирает то или иное решение, основываясь на своем опыте, на задачах, которые стоят перед системой, и на ресурсах, которыми он располагает. Кому-то нравятся бесплатные решения, потому что не нужно объяснять своему руководителю, на что тебе потребовались $N000 в год и почему нельзя без этого обойтись. Кого-то мотивирует наличие community или опыт огромного числа команд, которые этими решениями уже воспользовались и довольны результатом. Количество точек зрения стремится к количеству людей, которые задавались этим вопросом. Я не могу сказать, что чей-то аргумент верный, или чье-то возражение несущественно. Зато, каких бы взглядов ни придерживался разработчик, столкнувшийся с такой проблемой, большинство согласится, что по большому счету все популярные решения, представленные на рынке, отличаются лишь удобством настройки, интеграцией со смежными системами, возможностями расширения и поддержкой со стороны community либо разработчиков системы.

В общем, выбор билд-сервера - это тема для отдельного холивара. Скажу лишь, что мы выбрали Atlassian’овское решение Bamboo Build Server. Основных причин несколько, одна из них - простота интеграции с issue tracker’ом, который мы используем в проекте, а также с системами code review и хостинга репозиториев. В этом ребята молодцы: все удобно, все под рукой, и, что самое важное, практически все предусмотренные решения и опции отлично вписываются в процесс разработки нашей команды

Bamboo

Bamboo - весьма распространенное решение, его использует огромное количество команд во всем мире. Подробно со схемой работы этого CI/CD Tool можно ознакомиться на сайте официальной документации, я же позволю себе вольный перевод небольшой части этого документа во избежание разночтений в терминологии.

Задача Continuous Integration сервера заключается в том, чтобы выполнить всю работу по сборке, тестированию, деплою в тестовую среду проекта. CI сервер связывается с репозиторием, получает определенную ревизию проекта, выполняет все необходимые действия и предоставляет готовый результат сборки команде проекта.

Проект
  • состоит из одного или нескольких билд-планов
  • предоставляет отчет по всем билд-планам проекта
  • связан с другими приложениями (Jira, Fisheye, Crucible, Stash)
Билд-план
(Plan)
  • состоит из одной или нескольких стадий (stage)
  • все стадии запускаются последовательно, используют те же репозитории
  • содержит правила запуска билдов, зависимости от других билд-планов проекта
Стадия
(Stage)
  • состоит из одной или нескольких работ
  • выполняет работы параллельно, на свободных билд-агентахсчитается завершенной, когда все работы выполнены успешно
  • передает артефакты для последующих стадий сборки.
Работа
(Job)
  • состоит из одной или нескольких задач
  • все задачи внутри выполняются последовательно на одном и том же агенте
Задача
(Task)
  • дискретная операция, такая как чекаут ревизии проекта, запуск скрипта и т.д.


Более или менее схожее разделение доступно в любой системе сборки, оно обеспечивает необходимую гибкость в построении всего процесса. На первый взгляд кажется, что это чрезмерное усложнение. Так было и с нашим проектом, когда мы только начали использовать Bamboo, но постепенно все устаканилось, появилось четкое понимание того, какая часть всего процесса сборки должна быть масштабирована, какая должна оставаться изолированной, и в рамках предложенных концепций сложилась достаточно стройная структура.

В целом, нужно хорошо понимать, что билд-сервер или CI-сервер - это важная часть автоматизации процесса разработки ПО. Возлагая на этот модуль все задачи и работы, которые необходимо проделать на разных этапах и уровнях подготовки приложения к релизу в маркет, мы получаем своеобразный Application Release Pipeline. Он, в свою очередь, дает возможность легко определить, какие задачи вошли в тот или иной билд, на каком этапе сейчас релиз, какие проблемы возникают при интеграции нового функционала, на какой стадии подготовки hotfix’a мы сейчас находимся и многое другое.

Итак, мы плавно подошли к описанию того, как это сделано в нашей команде.

Задачи

Наш проект сборки разделен на несколько стадий, отражающих основные задачи на текущий момент:
  • Сборка - включает в себя все варианты сборки, которые могут понадобиться по ходу Release pipeline: alpha, beta, release. Да-да, у нас отличаются именно сборки проекта, а не только их статус. Различия продуктовые: различные ресурсы, наличие или отсутствие настроек и т.д.
  • Проверка - самая емкая и технически сложная часть всего этапа сборки приложения: статический анализ кода, юнит-тестирование, функциональное UI-тестирование, проверка локализации.
  • Деплой. В настоящий момент абстрагированн от всей сборки, он как бы находится сбоку. Таким образом, в случае необходимости мы можем задеплоить в любую среду (альфа, бета, релиз) какую угодно ревизию/ветку/тип приложения.
На этом, в принципе, можно и закончить рассказ, но я, пожалуй, проявлю назойливость и предоставлю подробности.

Сборка

Сейчас мы разрабатываем сразу три проекта с единой code base, назовем их Проект 1, Проект 2 и Проект 3. Конечно, различия между ними не такие радикальные, как между шахматами и видеоплеером, так как все три продукта относятся к категории почтовых клиентов. Тем не менее, у них разный дизайн, есть отличия в функционале, они по-разному взаимодействуют с сервером. Все это диктует свои требования к сборке, тестированию и разработке продукта.

Feature Branch Workflow

Любая сборка начинается с чекаута ревизии проекта из системы контроля версий. Казалось бы, зачем на этом заострять внимание - ведь чекаут может сделать каждый? Действительно. Но из какой ветки стоит это сделать?

Мы для работы над продуктом используем Feature Branch Workflow. Об этом подходе можно почитать отдельно. Главное его преимущество для меня - изолированность изменений. При таком подходе каждый разработчик может в рамках задачи перевернуть хоть весь проект, отдать это в тестирование, и если QA даст аппрув, то проверенный и функционирующий код попадет в общую ветку. Данный подход минимизирует риски попадания дефекта в релиз, за счет того, что определена последовательность действий: сначала проверка, потом мердж в основную ветку проекта.

Чтобы проверить эти изолированные изменения, у нас должна быть сборка, на которой мы сможем прогнать автотесты, и которую мы отдадим в ручное тестирование на одобрение от команды QA. Bamboo предоставляет из коробки необходимое для этого решение. Называется оно Branch Plan и заключается в том, что у билда определяется главная ветка (например, alpha), а все ветки, которые матчатся по указанному шаблону, рассматриваются как feature branch. Для них создается клон билд-плана, но с той разницей, что чекаут будет происходить из этой ветки, а не из главной ветки билд-плана. Выглядит это примерно так.

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

Сам branch plan выглядит так же, за исключением того, что имеет ссылку на задачу.

С таким флоу ветка неизбежно начинает устаревать с того момента, как ее создали. Для раннего обнаружения конфликтов с основной веткой, чтобы тестировался актуализированный код, нужно постоянно обновлять свою ветку во время разработки. Bamboo умеет это делать автоматически перед тем, как начнет собирать проект. В случае конфликта сборка не испечется, а разработчик должен будет сначала проапдейтить свою ветку и потом уже пушнуть эти изменения. Тогда перед сборкой конфликта не будет, и все пойдет своим чередом.

Product Flavors

Допустим, у нас есть проект, который нужно собирать в нескольких вариациях, изменяя ресурсы, код и конфиги. Существует несколько вариантов того, как это реализовать. Мы руководствовались тем, что все условия сборки, все конфигурации и прочая описательная часть должны находиться в скрипте сборки. В нашем случае для решения этой задачи идеально подходит Gradle . Для него есть хороший Android-плагин, который позволяет гибко настраивать большинство стандартных и нестандартных параметров для сборки сложного проекта.

Давайте разберемся, сколько вариантов сборки мы активно используем и поддерживаем.

Начнем с того что у нас три основных Product Flavors: Проект 1, Проект 2 и Проект 3.

Product Flavor - это представление ветки продукта. В большинстве случаев это разные приложения, которые имеют разные пакеты, разные сертификаты подписи, разные исходники и ресурсы. Для каждого приложения у нас есть несколько вариантов сборки, а именно:

  • debug - подписывается дебажным ключом, может отлаживаться, не обфусцируется;
  • alpha/branch alpha - обфусцированная сборка, отличается конфигами для аналитики, сборками крешей, ресурсами, дебажными настройками, доступными в приложении;
  • beta corp - бета-версия, которая имеет включенные логи, доступный отладочный режим;
  • beta - максимально приближенная к релизу сборка, которая отличается аналитикой, сборкой крешей, имеет выключенные логи, отладочный режим и не имеет дебажных настроек;
  • release - продакшн-версия приложения, практически все дополнительные опции выключены, аналитика и сбор статистики настроены на боевые проекты в этих системах и т.д.;
  • unit/UI testing - сборки, которые имеют перезаписанные манифесты, что позволяет, например, включить разрешения на чтение SMS, необходимых для автоматизированного тестирования входа (авторизация, регистрация, двухфакторная авторизация) с использованием SMS-кода.
Итого:

8 Build Types * 3 Product Flavors = 24 Application Variants

Зачем столько? Попробую ответить. Одна из типичных задач, которую приходится решать, имея три разных продукта, которые публикуются в разные среды, - разделять аналитику. И делать это необходимо, иначе статистика из альфа-версии приложения будет искажать существующую в продакшне картину. Для сбора статистики по крешам мы используем HockeyApp . В нем у нас раздельные проекты по разным вариантам сборки. Это позволяет легко отделять, например, креши Проекта 1 от крешей Проекта 2, бета-версию от релизной версии и т.д.

В build.gradle нашего проекта этот конфиг выглядит следующим образом.

ProductFlavors { project1 { ... android.buildTypes { alpha { hockeyApp { } } beta { hockeyApp { } } publicBeta { ... } release { ... } } } project2 { ... android.buildTypes { alpha { hockeyApp { } } ... } } project3 { ... android.buildTypes { alpha { hockeyApp { } } ... } }
Таким образом, мы можем конфигурировать различные значения для любых вариантов сборки. Что касается ресурсов и исходников, здесь используется примерно тот же принцип, за исключением одной особенности: есть возможность мерджить ресурсы из разных вариантов. В нашем проекте есть ресурсы, одинаковые для всех приложений - например, разметка экрана написания письма. Если бы такие файлы приходилось копировать в каждый пакет ресурсов и держать отдельно, то при изменении верстки экрана написания письма нужно было бы менять аж три файла. К счастью, gradle + android plugin умеют делать мердж ресурсов.

Расскажу чуть подробнее, как это происходит - возможно, кто-то сможет решить свои повседневные задачи, используя тот же подход.

Мы определили несколько папок с ресурсами (все они лежат в корне проекта).

  • res - общие ресурсы для всех вариантов приложения: здесь лежат общие селекторы, разметки, темы, стили и т.д.;
  • res_project1 - ресурсы, которые уникальны для Проекта 1: сюда попадает практически вся графика, которая используется в приложении, строки, которые содержат название проекта, специфичные логотипы или разметку - в общем, все, что относится только к Проекту 1;
  • res_project23 - здесь немного другая картина: в пакет res_ project23 попадают все ресурсы, которые не пересекаются с Проектом, но одинаковы для Проекта 2 и Проекта 3. Подобная группировка ресурсов помогает решить проблему, когда продуктово Проекты 2 и 3 очень похожи между собой, при этом достаточно сильно отличаются от Проекта 1. В противном случае, пришлось бы копировать одни и те же ресурсы в папки res_project2 и res_project3;
  • res_ project2 - ресурсы, уникальные для Проекта 2: на данный момент это цвета, графика, тексты. Все остальное лежит в общих пакетах;
  • res_ project3 - аналогично для Проекта 3, в этом пакете остается только уникальная подборка ресурсов для этого приложения.

В результате для каждого билд-варианта мы мерджим несколько пакетов, чтобы получить общий набор ресурсов для приложения:

  • Проект 1 = res + res_project1;
  • Проект 2 = res + res_project23 + res_ project2;
  • Проект 3 = res + res_project23 + res_ project3.
Это основа. Для более глубокой кастомизации можно, например, добавить специфичные ресурсы, код для тестовой сборки и т.д. Весь closure с исходниками выглядит у нас примерно так:

SourceSets { main { manifest.srcFile "AndroidManifest.xml" java { srcDir "src" exclude "**/instrumentTest/**" } resources.srcDirs = ["src"] aidl.srcDirs = ["src"] renderscript.srcDirs = ["src"] res.srcDirs = ["res"] assets.srcDirs = ["assets"] } androidTest { manifest.srcFile "src/instrumentTest/AndroidManifest.xml" java.srcDir "src/instrumentTest/Java" } project2 { res.srcDirs = ["res_project2", "res_project23"] java.srcDirs = ["src_common"] assets.srcDirs=["assets_ project2] manifest.srcFile "res_ project23/AndroidManifest.xml" } project3 { res.srcDirs = ["res_project3", "res_ project23] assets.srcDirs=["assets_project3"] java.srcDirs = ["src_project3"] manifest.srcFile "res_ project23/AndroidManifest.xml" } project1 { res.srcDirs = ["res_project1"] java.srcDirs = ["src_common"] assets.srcDirs=["assets_project1"] manifest.srcFile "res_project1/AndroidManifest.xml" } testingUi { manifest.srcFile "ui_testing/AndroidManifest.xml" } }
Осталось дело за малым. В конфиге билд-проекта нужно запустить правильный таск для того, чтобы получить желаемую.apk, например, gradle assembleProject1PublicBeta. Естественно, при наличии такого большого числа вариантов сборки мы решили не собирать их все последовательно, а распараллелить этот процесс. Итого мы получили 6 параллельных работ, которые выполняются в рамках стадии сборки. Каждая работа публикует 3 артефакта на каждый продукт.

Предполагаю, что у тех, кто дочитал до этого момента, появился вопрос: а зачем же собирать бету и релиз при каждой сборке проекта? Вопрос, действительно, очень интересный. Мы пришли к этому решению не сразу, а спустя много времени. Исторически бета и релизные сборки строились отдельно, используя ту же ревизию либо контракт о том, что код там тот же самый. Потом мы поняли, что такой подход таит в себе много проблем, а самая неприятная заключается в том, что статус сборки ты узнаешь уже после того, как принял решение опубликовать бету. По закону Мерфи, естественно, билд оказывается красным. По любой причине. Чем больше изменений, тем больше вероятность того, что они негативно повлияют на сборку, причем мы не можем ничего с этим сделать. Можно только сократить временной интервал между моментом внесения ошибки и моментом ее обнаружения. А в идеальном случае еще и сделать это изолированно от общей ветки. Если абстрагироваться от проекта и сборки именно бета или релизной версии и посмотреть на процесс автоматизации, то одним из основных показателей качества всего подхода к автоматизации билд-процесса мне сейчас видится, наверное, возможность узнать о возникших проблемах максимально быстро, а самое главное - узнать ДО того, как эти изменения попали в общую ветку.

Проверка

Автоматическая проверка качества в мобильных приложениях - это, безусловно, тренд последнего года. По моему опыту, для многих это остается чем-то нереальным. Все об этом говорят, но практически никто не видел. Мы занимаемся такими задачами в рамках нашего проекта уже 2 года, и за это время уже сложилось довольно четкое понимание большинства тонкостей, с которыми приходится сталкиваться любому разработчику. Все эти проблемы и решения являются достаточно новым и неустоявшимся сегментом для мобильных приложений, хотя веб уже давно прошел этот путь и имеет достаточное количество стандартизированных решений.

Первый вопрос, который возникает у большинства: что же мы будем автоматизировать? Разработчик ответит: мы будем тестировать код, менеджер тут же начнет спорить, что тестировать нужно функционал. Я считаю, что тестировать нужно и то и другое.

Вообще, если говорить о нашем приложении, то все проверки делятся на несколько категорий:

  • Статический анализ : я не знаю, почему этому подходу уделяется так мало внимания, это очень мощный инструмент, позволяющий применять формализованные правила ко всему проекту, а не к отдельным классам;
  • UnitTesting : старые добрые юнит-тесты, которые позволяют убедиться в том, что класс работает именно так, как ожидает разработчик или пользователь этого класса;
  • UiTesting : функциональные/end-to-end тесты, которые проверяют конечный результат: то, что увидит пользователь, и как он будет с этим работать.

Статический анализ

В качестве статического анализатора мы используем уже готовые решения. Для Android это Lint, который стал за последнее время весьма эффективным инструментом, позволяющим следить за качеством android-specific кода, ресурсов разметки, графики и т.д. Помимо всего прочего, он позволяет добавлять свои проверки, специфичные для контракта внутри проекта. Одним из таких контрактов является то, что никакие layout-related параметры не должны находиться в стилях. Например, свойства layout_margin\ layout_alignParentTop или что-то подобное. С точки зрения синтаксиса никто не запрещает выносить эти свойства в стили, но в таком случае сам стиль используется не для определения визуальной составляющей какого-то UI-компонента, а для хранения каких-то значений, которые потом можно не писать в файле разметки. Другими словами, стиль используется как контейнер атрибутов. Мы решили, что это разные вещи, которые стоит разделять, потому что, во-первых, LayoutParams все-таки относятся к разметке, а во-вторых, они относятся не к тому контролу, в тэге которого эти атрибуты прописываются, а к его паренту, в котором он лежит.

Если разобраться, то в любом более или менее состоявшемся проекте, в котором есть гайды по написанию кода, ресурсов разметки, присутствуют шаблоны для решения типичных для этого приложения задач, таких вещей достаточно много. Их можно отслеживать на этапе код-ревью, документировать, напоминать о них каждый раз в начале рабочего дня или же рассчитывать на то, что, однажды ознакомившись с этими пожеланиями, в дальнейшем все будут их выполнять. Как говорится, блажен кто верует, но лично мне намного спокойнее работать, зная, что я сам об этом не забуду и ничего не пропущу, торопясь скорее закрыть надоевший таск. Нужно формализовать такие проверки, добавить их в процесс сборки с удобными отчетами и не беспокоиться о том, что, взявшись за новую задачу, ты неожиданно обнаружишь проскочивший все проверки код, от которого волосы дыбом встают.

Писать свои проверки достаточно легко, даже увлекательно. По ходу добавления любых статических проверок сразу появляется куча идей, как статически выявлять какие-то другие проблемы. Для Lint в этом помогут гайды и официальная документация. Разрабатывать правила можно прямо в Android Studio.

Для java-кода также есть давно изобретенные статические анализаторы. Я не буду перечислять все, расскажу только о том, что мы используем FindBugs. Когда мы выбирали инструмент, то хотели получить удобный формат, достаточный объем правил, по которым будет осуществляться проверка, и возможность добавления своих правил. На данный момент мы написали необходимые проверки, такие как проверка закрытых курсоров, проверка того, что инстанс AccountManager’a получается всегда с application context’ом, проверка того, что обязательный для вызова метод onEventComplete вызывается при шаблонных использованиях класса событий, и другие. Добавлять свои правила, которые будут определять внутрикомандные договоренности, предотвращать распространенные ошибки из-за невнимательности - это отличная практика, которая сокращает время на код-ревью и тестирование, а также гарантирует, что такие ошибки как минимум не попадут в будущем в продакшн-версию приложения. В качестве пособия по написанию проверок мы использовали статью FindBugs, Part 2: Writing custom detectors . Там наглядно показано, как создавать свой плагин, добавлять детекторы и использовать это в процессе проверки. Отчет предоставляется либо в отформатированном HTML-документе, либо в виде XML-отчета, где коротко и по делу написано, в каком классе/методе найдена ошибка, код ошибки, строка и т.д. Этого обычно достаточно, чтобы понять, где ты только что не убрал за собой:-).

Замечательно, не правда ли? Огромный набор правил и распространенных ошибок уже готов, есть и возможность его дополнить, остается только найти в себе смелость и приступить к использованию.

Однажды я обратил внимание на то, что в нашем проекте используются SNAPSHOT версии библиотек. Очевидно, что это допустимо только в бранче для задачи, когда эти изменения вносятся в используемую библиотеку. После того как код влит в основную ветку, никаких SNAPSHOT’ов в проекте быть не должно. В данном случае причина достаточно прозаична и характеризует большинство подобных ошибок. После того как задачу протестировали и решили, что эта версия достигла всех definition of done, разработчик был настолько счастлив, что забыл влить библиотеку в основную ветку, определить новую версию этой библиотеки и поменять версию в основном проекте. Проблема в том, что ни Lint, ни FindBugs не могут проверить скрипт сборки. Более того, даже если эти проверки добавить в сам build.gradle, то необходимо знать, где это допустимо, а где нет. Очевидно, что это допустимо в бранче, в котором сейчас и меняется библиотека, но недопустимо после того, как он попадает в общую ветку. Вот так мы начали использовать git pre-receive хуки для того, чтобы следить за происходящим в проекте на уровне репозитория.

Я знаю, что многие команды не считают нужным тратить время на настройку правил, подходящих для проекта на уровне системы контроля версий, т. к. “у нас нет дураков, никто не будет удалять все ветки в репозитории”, или по каким-то другим причинам, например, из-за нехватки времени. Для нас это пройденный этап: мы пришли к решению, что лучше потратить немного больше времени, но быть уверенными в безопасности и качестве продукта. Для этих целей pre-receive хуки подходят очень хорошо: мы можем определить, что изменения добавляются в общую ветку, и проверить, что HEAD этой общей ветки не содержит нежелательного кода. В лучшем случае никто никогда не узнает о наличии такой проверки, но, как показывает практика, достаточно случайной ошибки, чтобы появилась возможность знатно проколоться. Pre-receive hook отлично подойдет для проверки всех исправленных TODO и FIXME, которые разработчик охотно расставляет, но забывает исправить. Он, также, прекрасно справляется с типичными проблемами логирования, - добавлением вывода new Throwable() ко всем интересующим разработчика функциям, потому что в ветке нашелся очень сложный и требующий множества деталей баг. Для нас, возможность отслеживать совершенные ошибки автоматически важна для понимания того, что мы не наступим еще раз на те же грабли. Ошибки совершают все, важно лишь какие выводы ты после этого делаешь. Наш вывод состоит в том, что помимо исправления, нужно приложить усилия к тому, чтобы эти ошибки впредь не совершать.

Unit Testing

Здесь по большому счету все обыденно. Для некоторых классов написаны проверки, которые позволяют убедиться, что класс работает именно так, как задумывалось, и заодно показывают клиенту класса пример, как им можно воспользоваться. В данный момент юнит-тесты запускаются на реальных устройствах но не устанавливают реальное соединение, если это необходимо. Кстати, о необходимости устанавливать соединение: когда разработчик размышляет, как тестировать тот или иной модуль, чаще всего он первым делом думает, каким образом подменить зависимости класса, чтобы изолировать тестирование от действующей среды. В случае с сетевым соединением это может показаться непростой задачей, потому что сетевое взаимодействие не заменяется вызовом одного метода, требует мокать целый слой логики. Мы некоторое время сопротивлялись тому, чтобы с помощью хука в коде приложения подменять ответ сервера и выполнять все последующие действия с ним. Дело в том, что такой подход увеличивает риск того, что тестироваться будет не тот код, который работает в боевом приложении. Каждый раз, когда встает вопрос, стоит ли менять интерфейс класса для удобства тестирования, стоит ли добавлять в процесс выполнения функции дополнительные условия, чтобы «замокать» какие-то зависимости, я стараюсь придерживаться следующей позиции: в первую очередь весь написанный код должен быть безопасен с точки зрения функций приложения. Если дополнительные условия, добавленные в код, требуют отдельной проверки, то для тестов не нужно этого делать. Это главная причина, по которой нас не устроил обычный сеттер, который бы просто заменял ответ, брал бы его из другого источника.

В итоге мы пришли к другому решению, на мой взгляд, более честному. Вот так выглядит один из тестов, который проверяет, что при определенном ответе команда выдает статус “error_folder_not_exist”

@AcquireCookie @LargeTest public void testDeleteNonExistingFolder() { DeleteFolder delete = runDeleteFolder(999); assertERROR_FOLDER_NOT_EXIST(delete); }
В этом тесте мы делаем честный запрос на сервер, то есть команда работает абсолютно так же, как и в приложении. Проблема в том, что юнит-тест зависит от того, как настроена сеть на устройстве, на котором он выполняется. А ниже идет второй тест, который проверяет абсолютно то же самое, но уже подставляя желаемый ответ, не выполняя реального запроса и не взаимодействуя с сервером.

@MockMethod(response = RESPONSE_NOT_EXISTS) public void testDeleteNonExistingFolderMock() { testDeleteNonExistingFolder(); }
Таким образом, мы имеем возможность управлять выполнением тестов - это нужно, например, для того, чтобы статус билда не учитывал ответ сервера. Мы полагаемся на то, что протокол взаимодействия описан, и, убедившись в корректности формирования запроса (с помощью юнит-тестов, разумеется), можем быть уверены, что сервер даст корректный ответ. А при корректном ответе остается лишь убедиться в том, что приложение его интерпретирует соответствующим образом. Однако, например, для ночной сборки неплохо было бы убедиться еще и в том, что не нарушен контракт взаимодействия с сервером. Для этого будут запускаться все тесты, в том числе и те, которые реально с ним взаимодействуют. Это даст нам дополнительную подушку безопасности на случай, если из-за какого-то бага контракт взаимодействия с сервером нарушен. Мы узнаем об этом из результатов тестов, а не из пользовательских отзывов в маркете. Если для нас так важно проверять функционал от начала до конца, то можно сделать эти тесты основными и выполнять их для каждой сборки приложения.

Дело в том, что мы не хотим постоянно зависеть от сервиса, но при этом нам нужно мониторить ситуацию и в виде ежедневных отчетов получать информацию о том, что все хорошо, или же о том, что какая-то часть приложения не в порядке. Здесь я предпочитаю разделять наше приложение и сторонние сервисы, которые критичны для его полноценной работы, но не являются зоной нашей ответственности. Мы можем обнаружить в нашем приложении проблему, связанную с работой стороннего сервиса, но не можем ее исправить. Наша задача состоит в том, чтобы сообщить о проблеме, дождаться исправления и прогнать тесты на работу с этим сервисом, чтобы убедиться в том, что проблема устранена.

UI Testing

С точки зрения пользователя, это самые честные тесты. С точки зрения разработчика - самые сложные. Самые честные, потому что тестируют конечный продукт, а не какую-то его часть. Свалить вину на кого-то другого не выйдет: любой баг - это баг приложения, и неважно, в чем его причина, в несовершенстве Android в кривых руках другого разработчика или в чем-то еще. В любом случае, ошибку нужно исправить. К преимуществам такого black-box тестирования можно отнести и то, что для нас, по сути, нет разницы, как реализован функционал, какая архитектура у приложения и т. д. Если два бага в приложении наложились друг на друга, и в итоге пользователь увидел правильный результат - нас это устраивает. Если во всем множестве случаев баги приложения позволяют получать корректные результаты для пользователя - нас это устраивает с точки зрения проверки функционала.

Если юнит-тесты проверяют, работает ли код именно так, как задумывал разработчик, то UI-тесты скорее предназначены для того, чтобы продуктовая команда убедилась в корректности выполнения пользовательских сценариев в приложении.

На пользовательские сценарии также завязаны и баги. Один из лучших способов исправить баг приложения, получив сценарий его воспроизведения, - написать тест. Убедиться в том, что проблема присутствует. Исправить проблему в коде приложения (если нужно, то сопроводить дополнительными юнит-тестами на модуль, в котором какое-либо поведение не было закрыто) и удостовериться, что теперь тест проходит успешно. В этом случае нет необходимости задействовать весь бюрократический аппарат, в котором несколько человек должны проаппрувить, что баг исправлен, что ничего нового не сломалось и т. д. Вся соль в том, что если тест написан вдумчиво, и именно с целью обнаружения и выявления проблемы, то в случае его успешного прохождения уже есть повод считать проблему решенной. Чем больше таких кейсов закрыто тестами, тем качественнее проверка, потому что тем выше вероятность, что мы ничего не упустили.

Конечно, сделать все это намного сложнее, чем сказать. Но обо всем по порядку: начнем с выбора фреймворка, на основе которого мы будем строить свои тесты. Опять же, я не открою Америку, написав, что фреймворков сейчас достаточно много, но нельзя посоветовать какой-то один, который решит 99% всех проблем. Можно начать писать собственный идеальный фреймворк, сделав ставку на то, что ваш коэффициент кривизны рук меньше, чем у конкурентов, и надеясь, что через месяц все ваши проблемы будут решены, а через полгода, посчитав стоимость такого решения, вернуться к этому выбору вновь. Я не очень люблю делать чужую работу, и, возможно, поэтому считаю такой подход утопическим. Утопией мне видится и кроссплатформенное тестирование, потому что слишком велики различия между Android и iOS. Написать один набор тестов, который будет проверять и одно приложение, и другое, кажется очевидным решением только на первый взгляд. Разная навигация внутри приложения, разная разметка в рамках одного экрана, разное поведение системы в ответ на сворачивание приложения, не говоря уже о том, что даже функционал может отличаться, потому что любой качественный продукт будет учитывать все особенности платформы для того, чтобы предоставить пользователю наилучший опыт.

Исторически мы в проекте использовали Robotium. Это весьма известное решение, которое используется большим количеством команд как у нас в стране, так и за рубежом. Интересно, что всех его пользователей объединяет горячая нелюбовь к этому самому фреймворку. Он медленный, нестабильный, на нем неудобно писать тесты. Но все, тем не менее, регулярно возвращаются к его использованию. То ли дело Espresso! Он быстр как ветер, стабилен как экономика Соединенных Штатов и т. д. Вот что делает репутация поискового гиганта с проектами, которые он взял под свое крыло. Мы писали на Robotium 2 года, поэтому я могу довольно уверенно сказать, что ответственность за нестабильность и низкую скорость лежит скорее на клиенте, который пишет эти тесты. Давайте в этом разберемся. Причина проблем со скоростью зачастую кроется не в несовершенстве алгоритмов Robotium, не в его архитектуре, а в том что в тестах есть злоупотребление так называемым Sleep Pattern’ом. Суть его заключается в том, что любую проблему можно решить, добавив sleep(N * 1000) перед той строчкой, где проблема была обнаружена. В основе этого лежит следующая простая вещь: тесты выполняются в потоке, отличном от главного потока приложения (UI Thread). Соответственно, синхронизация, которая выполнена с помощью Sleep(), не является хорошим решением проблемы. Отсюда итог: хоть 10 секунд жди между шагами в тестах, результат гарантирован не будет. В Instrumentation-based тестах есть штука, которая ждет, когда же UI Thread приложения закончит операции, которые сейчас находятся в процессе. Класс android.app.Instrumentation имеет метод:

/** * Synchronously wait for the application to be idle. Can not be called * from the main application thread -- use {@link #start} to execute * instrumentation in its own thread. */ public void waitForIdleSync() { validateNotAppThread(); Idler idler = new Idler(null); mMessageQueue.addIdleHandler(idler); mThread.getHandler().post(new EmptyRunnable()); idler.waitForIdle(); }
Его использование, как мы на своем опыте убедились, решает большинство проблем с тем, что View не найдена, хотя скриншоты показывают, что все отображается, а также с тем, что View находится в промежуточном состоянии, анимируя свои свойства от одного значения к другому и т.д.

Естественно, истории о том, что Espresso многократно лучше, не давали нам покоя. План по переходу на этот фреймворк созрел давно; к тому же Google сейчас уделяет достаточно много внимания вопросу автоматизированного тестирования, поэтому есть предпосылки к тому, что Espresso будет развиваться активнее. Решимости добавили и убеждения Lead developer’а в том, что для перехода с Robotium на Espresso достаточно поменять TestRunner. Мы попробовали, и тесты действительно работали. Теперь мы можем, не меняя старых тестов за раз, писать новые сценарии и при этом пользоваться всеми преимуществами Espresso. Для нас это определило переход на рельсы нового фреймворка. Мы запустили тесты и замерли в ожидании результатов.

Espresso действительно оказался быстрее, хотя драматических изменений не произошло. Сейчас все наши тесты разбиты на ~26 пакетов, и в каждом было замечено ускорение. Но суммарные изменения в скорости прохождения тестов укладываются в 4%. На мой взгляд, это не является существенным преимуществом. Намного больше, с моей точки зрения, дает возможность писать аналог waitForIdleSync для любого ожидания в приложении: не только для задач интерфейса и анимации, но и для задач загрузки данных из сети и с диска - для любых взаимодействий, результатом которых мы должны оперировать, делая проверку в коде теста. Эта фича называется CustomIdlingResource и она действительно очень выгодно выделяет Espresso по сравнению с Robotium. Несмотря на то, что идея очень простая, а именно - дать возможность зарегистрировать свою реализацию интерфейса ожидания idle состояния, custom idling resource позволяет управлять синхронизацией между тестами и приложением. Таким образом можно дожидаться, когда в приложении пройдут все асинхронные операции, например, что в совокупности с idle состояние главного потока, говорит о том, что ожидание можно заканчивать и приступать к проверкам состояния приложения.

Естественно, Espresso - это не сказочный джинн. Он не может решить все ваши проблемы, он не способен написать тесты за вас, и он не в силах выполнять задачи по поддержанию тестовой инфраструктуры, запуску тестов и сбору отчетов.

Помимо тестирования непосредственно функционала внутри приложения, распространенная задача, которую приходится решать в контексте автоматизированной проверки качества, - это взаимодействие вашего продукта с другими приложениями, которые могут быть установлены на телефоне пользователя. В качестве примера можно взять, например, Sharing из другого приложения (для почтового клиента это достаточно актуально) или статус-бар уведомления. В обоих случаях сценарий затрагивает другое приложение, которое работает в другом процессе. Все Robotium/Espresso-подобные фреймворки становятся слепыми, как только дело касается другого процесса. К счастью, решение, которое позволяет писать cross-app functional UI tests, уже существует и называется UI Automator. Если раньше пришлось бы выбирать между тем или иным фреймворком либо поддерживать разные проекты, каждый из которых будет заточен под разные проверки, то с релизом Testing Support Library, анонсированной на минувшей конференции Google I/O 2015, мы можем объединить преимущества каждого подхода и использовать тот инструментарий, который потребуется в каждом отдельном случае. Это означает, что для почтового клиента, к примеру, мы можем автоматизировать такой сценарий:

  1. Запустить приложение, перейти в список писем.
  2. Открыть написание нового письма, ввести тему, получателя, прикрепить вложения.
  3. Получить push-уведомление на подключенный ящик.
  4. Перейти по уведомлению, проверить содержимое нового письма.
  5. Выйти с экрана нового письма по back-кнопке.Удостовериться, что мы вернулись на экран написания письма, и что все заполненные поля сохранены.
В данном примере мы можем спокойно пользоваться прежним фреймворком для того, чтобы переходить по моделям списка писем, чтения письма, написания нового письма и т.д., а для шагов 3 и 4 мы можем использовать uiAutomator framework, чтобы проверить текст уведомления, убедиться, что в уведомлении присутствуют необходимые кнопки, и перейти по уведомлению в приложение. Затем тест будет дальше использовать API Espresso, и нам не придется писать еще одну реализацию существующих моделей под второй фреймворк. Для меня как для разработчика это лучшая новость, которая могла появиться в контексте библиотек для автоматизации тестирования.

Конечно, все это кажется несложным лишь на первый взгляд. За кадром осталась куча собранных граблей и наступание в самые неприятные субстанции - порой казалось, что вся эта идея просто не может быть реализована.

В дальнейшем мы отдельно расскажем о том, как выстроена инфраструктура, как обеспечивается круглосуточная готовность устройств к работе, как распределяются тесты на разные product flavors по разным сборкам, как тестируются разные реализации в зависимости от версии операционной системы, форм-фактора устройства и т.д. Ведь у нас за плечами такие долгие два года борьбы с adb, usb, VirtualBox и многими другими инструментами и технологиями. Впереди осталось больше работы, чем уже проделано, но мы понимаем, что все это было не зря.

Разработка мобильных приложений всегда ассоциируется с необходимостью изучения дополнительных технологий. А что если пересмотреть вопрос и воспользоваться уже знакомыми инструментами?

Впервые фирма «1С» попыталась выйти на рынок мобильной разработки в 2006 году. В то время был настоящий ажиотаж на автоматизацию работы удаленных сотрудников при помощи КПК. Новые программы для решения подобных задач появлялись как грибы и такой вендор как «1С» с успешными продуктами для автоматизации разных сфер бизнес не мог упустить шанс выйти на прибыльный рынок.

К середине 2006 года компания представила релиз нового продукта с многообещающим названием «1С:Предприятие 8. Расширение для карманных компьютеров». У разработчиков 1С, разглядевших перспективы 8-й платформы появилась надежда, что теперь на одном инструменте без особого труда стало возможным выполнять разработку под популярную в те годы мобильную операционную систему «Windows Mobile».

На практике все выглядело значительно хуже. Воплощать оригинальные идеи этот инструмент не позволял. Пакет «Расширение для карманных компьютеров » представлял скорей дополнение для определенных типовых конфигураций, чем полноценное решение для разработки. Расширить функционал конфигурации путем добавления новых объектов метаданных было не предусмотрено. На откуп сторонним программистам оставались совсем простые вещи: создание новых форм для взаимодействия с пользователем, обработка пользовательских событий.

Да, были всевозможные обходы ограничений, но даже они не позволяли по-настоящему развернуться. Помимо технических ограничений, потребители ощутили серьезный финансовый барьер. Компаниям, решившим внедрить решение от «1С», требовалось закупить производительные КПК, приобрести лицензии на Windows Mobile, а также заплатить «1с» за поставку решения и конечное приложение.

Решение от «1С» обходилось слишком дорого. Компании, привыкшие экономить, продолжали использовать альтернативные решения. Тем более, разработчики альтернатив успели снабдить свои продукты функционалом для взаимодействия с типовыми решениями «1С».

Технические ограничения, высокая стоимость не позволили продукту повторить колоссальный успех десктопной платформы. Идея завоевать мобильный рынок корп.

приложений с треском провалилась.

Шаг вперед

Проигрыш и убытки от неудачного проекта не поставили окончательную точку на развитии перспективного направления. В 2013 году, компания «1С» представила первую стабильную версию новой платформы 8.3, обладающую функцией разработки мобильных приложений.

«1С» полностью переосмыслила подход к решению мобильной «теоремы» и учла ошибки предыдущего неудачного продукта. Результатом стал абсолютно новый инструмент, не имеющий ничего общего с предшественником и ориентированный на самые актуальные мобильные платформы – Android и iOS.

Мобильные приложения в стиле 1С

Для полноценного знакомства с возможностями разработки под мобильные платформы попробуем разработать небольшую конфигурацию. На сквозном примере вы сможете лучше оценить доступный функционал и определиться с возможностью использования платформы «1С» для решения задач.

Для работы вам потребуется последний релиз платформы «1С:Предприятие 8.3». Учебная версия дистрибутива доступна на официальном сайте 1С. Для воссоздания примера ее возможностей более, чем достаточно.

Помимо платформы «1С:Предприятие 8.3» нам потребуются ряд дополнительных инструментов. В статье будет рассматриваться пример разработки приложения для Android. В связи с этим придется загрузить: Android SDK и WEB-сервер Apache . Первый компонент содержит все необходимое для сборки приложения и эмулятор для тестирования, а WEB-сервер пригодится для быстрой загрузки приложения на мобильную ОС.

Также нам потребуется поставка «Мобильной платформы разработчика». Она содержит конфигурацию для упрощения процесса сборки созданного мобильного приложения, а также мобильную платформу разработчика. Ее необходимо установить на мобильном устройстве или эмуляторе.

Для сборки приложения, готового к распространению через Google Play потребуется загрузить Apacheant и JavaJDK . Эта тема выходит за рамки статьи, поэтому узнать подробности о работе с этими инструментами и сборкой приложения вы можете в соответствующем разделе моего .

Конфигурируем инструменты

Платформа «1С:Предприятие 8.3 » и web-сервер Apache поставляются с инсталляторами и устанавливаются стандартным образом. Android SDK необходимо просто разархивировать в отдельную директорию и запустить “sdk manager.exe ”. Перед вами появится окно с выбором доступных пакетов для установки. Для тестирования, рассматриваемого в рамках статьи примера, потребуется выбрать и установить: Android SDK Tools , Android Platform Tools , SDK Platform API 17 .

Последним шагом будет создание новой информационной базы. Для тех, кто не связан с разработкой под на «1С:Предприятие » поясню, что любое решение для этой платформы состоит из информационной базы и конфигурации. Добавление новой базы осуществляется по кнопке «Добавить » стартового окна. После добавления базы открывайте ее в режиме «Конфигуратор ».

Первая мобильная конфигурация

В главном меню конфигуратора найдем раздел «Конфигурация » и выберем пункт «Открыть конфигурацию». Дерево конфигурации (объектов, из которых будет состоять будущее приложение) отобразится в левой части окна. Выделите в нем корень конфигурации и нажмите сочетание клавиш «Alt + Enter ». В правой части окна конфигуратора откроется редактор свойств.

Назовем конфигурацию «TODO » и в свойстве «Назначение использования» укажем «Мобильное устройство ». Обратите внимание, выполнив последнее действие, некоторые узлы дерева конфигурации станут неактивными. К сожалению, воспользоваться всеми объектами метаданных на мобильной платформе не получится.

Для решения нашей задачи, в дереве конфигурации нам потребуется создать несколько объектов метаданных:


Процедура ДобавитьЗадачу(Задача) Экспорт МенеджерЗаписи = СоздатьМенеджерЗаписи(); МенеджерЗаписи.Период = ТекущаяДата(); МенеджерЗаписи.Задача = Задача; МенеджерЗаписи.Статус = Задача.Статус; МенеджерЗаписи.Записать(); КонецПроцедуры

Листинг 2. Код функции «ПолучитьСписокНеЗакрытых Задач()

Функция ПолучитьСписокНеЗакрытыхЗадач() Экспорт Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | СостояниеЗадачСрезПоследних.Задача КАК Задача, | СостояниеЗадачСрезПоследних.Задача.ДатаИсполнения КАК ДатаИсполнения |ИЗ | РегистрСведений.СостояниеЗадач.СрезПоследних(&ТекущаяДата, Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЗадач.Выполнена)) КАК СостояниеЗадачСрезПоследних | |УПОРЯДОЧИТЬ ПО | ДатаИсполнения УБЫВ"; Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата()); Возврат Запрос.Выполнить().Выгрузить(); КонецФункции

С получением данных из регистра сведений и их записью разобрались, теперь научим наш справочник работать с регистром. Для этого в дерево конфигурации добавим общий модуль с именем «РаботаСЗадачами ». Можно обойтись и без него, но мне сразу хочется акцентировать внимание на возможность разбивки кода по модулям. Многие 1С-разработчики до сих пор пренебрегают этой рекомендацией и всю логику описывают в одном месте, тем самым затрудняя последующее сопровождения кода. Создадим в модуле новую процедуру «СоздатьНовуюЗадачу » (см. листинг 3).

Листинг 3. Код процедуры «СоздатьНовуюЗадачу»

Процедура СоздатьНовуюЗадачу(Ссылка) Экспорт Если Ссылка.ЭтоГруппа Тогда Возврат; КонецЕсли; Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | СостояниеЗадачСрезПоследних.Статус |ИЗ | РегистрСведений.СостояниеЗадач.СрезПоследних(&ТекущаяДата, Задача = &Задача) КАК СостояниеЗадачСрезПоследних"; Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата()); Запрос.УстановитьПараметр("Задача", Ссылка); Результат = Запрос.Выполнить().Выбрать(); Если Результат.Следующий() Тогда Если Результат.Статус <> Ссылка.Статус Тогда РегистрыСведений.СостояниеЗадач.ДобавитьЗадачу(Ссылка); КонецЕсли; Иначе РегистрыСведений.СостояниеЗадач.ДобавитьЗадачу(Ссылка); КонецЕсли; КонецПроцедуры

Перед тем как создать новую запись выполняется проверка на наличие уже имеющихся записей по задаче. Если запись уже существует, то надо сравнить статусы задач. Если статус из регистра не отличается от статуса записываемого элемента, в создании дополнительной записи необходимость отсутствует.

Последним штрихом откроем форму элемента справочника «Задачи» и создадим обработчик события «ПослеЗаписиНаСервере ». В нем напишем вызов процедуры, описанной в третьем листинге:

РаботаСЗадачами.СоздатьНовуюЗадачу(ТекущийОбъект.Ссылка);

Работаем над интерфейсом

Основной функционал приложения готов – пользователь может создавать задачи, и каждая новая задача формирует запись в периодическом регистре сведений. Теперь займемся интерфейсом. Вынесем работу с задачами на передний план. Ведь логично сразу после запуска приложения отображать список не закрытых задач и возможность создать новую?

Найдем в дереве конфигурации узел «Общие формы » и добавим новую форму с именем «РабочийСтол ». Откроем созданную форму в конструкторе интерфейса и добавим реквизит типа «ТаблицаЗначений ». Назовем его «ОткрытыеЗачи». Таблица будет содержать две колонки – «Задача » (СправочникСсылка.Задачи) и «ДатаИсполнения » (Дата).

Следующим шагом перетащим добавленный реквизит на форму. У нас должен получиться интерфейс простейшей таблицы. Никакие размеры указывать не будем, оставим заботу о масштабировании интерфейса платформе.

Для созданной таблицы, в инспекторе свойств установим флажок для свойства «Только просмотр », а свойству «Положение Командной Панели » укажем значение «Нет». Мы будем заполнять таблицу динамическими сведениями, поэтому смысла в редактировании со стороны пользователя нет.

Теперь опишем у формы обработчик события «ПриСозданииНаСервере». Добавим в него одну строку кода:

ОткрытыеЗадачи.Загрузить(РегистрыСведений.СостояниеЗадач.ПолучитьСписокНеЗакрытыхЗадач());

В коде мы обращаемся к описанной нами процедуре «ПолучитьСписокНеЗакрытыхЗадач » и результат ее выполнения помещаем в таблицу.

Вернемся к конструктору формы и добавим группу типа «Обычная группа без отображения» с двумя кнопками: «Создать » и «Обновить ». Свойству «Группировка » у добавленной группы зададим значение «Горизонтальная». Для более выразительного оформления кнопок добавим изображения и изменим шрифт по умолчанию.

Теперь выделим кнопку «Создать » и зададим ей глобальную команду «Задачи: создать ». Это позволит создавать задачи, минуя вход в сам справочник. По нажатию второй кнопки будем обновлять содержимое таблицы с задачами. Для этого потребуется создать дополнительную команду формы.

Все новые команды формы создаются на одноименной вкладке «Команды ». Принцип прост – добавляем новую команду, описываем в ней код действия и затем связываем команду с интерфейсом, в нашем случае с кнопкой.

Не стоит также забывать, что мы разрабатываем управляемое приложение, поэтому надо четко разграничивать клиентский и серверный код. При нажатии кнопки будет контекст «НаКлиенте », а данные из базы будем получать уже с сервера. В коде это выглядит так:

&НаКлиенте Процедура ОбновитьСписокЗадач(Команда) ОбновитьСписок(); КонецПроцедуры &НаСервере Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) ОткрытыеЗадачи.Загрузить(РегистрыСведений.СостояниеЗадач.ПолучитьСписокНеЗакрытыхЗадач()); КонецПроцедуры

Теперь определим нашу форму рабочего стола в качестве области начальной страницы. Откроем свойства конфигурации (выделяем самый верхний узел и нажимаем «Alt+Enter ») и для свойства «Рабочая область начальной страницы» установим значение «Одна колонка », после добавим в список нашу форму «РабочийСтол ».

Приложение полностью готово и самое время протестировать его в работе. Попробуйте запустить пример и создать несколько задач со статусом отличным от «Выполнена ». Регистр сведений пополнился новыми записями (это можно посмотреть через пункт меню «Все функции ») и часть из них выводится на рабочем столе.

Приземляемся на Android

Конфигурация отлично работает на десктопе, и теперь самое время протестировать ее на эмуляторе мобильной ОС. Для подготовки нового эмулятора запустите командный интерпретатор (cmd.exe ) и перейдите в директорию «toos» дистрибутива с Android SDK. Выполните команду «android.bat avd », которая запустит менеджер виртуальных Android устройств. В нем нажмите кнопку «Create» и в появившемся окне укажите параметры виртуального устройства. В своем рабочем окружении я решил эмулировать Nexus S с Android версии 4.2.2. (API Level 17).

После создания устройства сразу же запустим его. Пока выполняется загрузка android, вернемся в конфигуратор и опубликуем наше приложение на web-сервере. В главном меню конфигуратора выбираем пункт «Конфигурация » -> «Мобильное приложение » -> «Публиковать ». В окне настроек публикации указываем имя приложения (может быть любым), web-сервер (в нашем окружении он должен быть один) и каталог для хранения настроек.

Указав в качестве имени «todo-mobile », приложение будет доступна по адресу – «http://host/todo-mobile ». Нажимаем «ok» и попробуем обратиться к опубликованному приложению с помощью браузера. В случае успеха, сервер отдаст XML-код созданной конфигурации.

Возвращаемся к эмулятору и загрузим в него приложение с мобильной платформой разработчика. Сам файл приложения доступен вместе с поставкой мобильной платформы разработчика и называется «1cem-arm.apk». Для установки этого приложения в эмуляторе воспользуемся утилитой «adb.exe » из директории «platform-tools »: adb.exe install –r 1cem-arm.apk .

После успешной установки, открываем в эмуляторе список приложений и запускаем мобильную платформу разработчика. В открывшемся окне нажимаем «Add application » и в поле «адрес» указываем URL к нашему web-серверу. У меня это http://192.0.168.106/todo-mobile . Нажимаем «Add » и наша конфигурация успешно перемещается на мобильную платформу. Приложение готово к работе. Протестируйте результат и возвращайтесь в конфигуратор, самое время снабдить приложения «мобильным функционалом».

Отправка SMS/MMS сообщений

Функции для работы с SMS/MMS сообщениями мобильными платформами поддерживаются по-разному. Например, при работе приложения на Android, у разработчика есть возможность оформить подписку на SMS и получать доступ к новым сообщениям сразу после получения. Увы, но на iOS эта же возможность отсутствует, поэтому во время разработки документация должна быть под рукой.

Для отправки SMS сообщений предусмотрен объект SMSСообщение . Рассмотрим пример:

&НаКлиенте Процедура ОтправитьSMSСообщение(Получатель, ТекстСообщения) НовоеСообщение = Новый SMSСообщение(); НовоеСообщение.Текст = ТекстСообщения; НовоеСообщение.Получатели.Добавить(Получатель); СредстваТелефонии.ПослатьSMS(НовоеСообщение); КонецПроцедуры

Код достаточно простой и вряд ли нуждается в комментариях. Теперь посмотрим на оформление подписки на входящие сообщения:

&НаКлиенте Процедура ПодключитьОбработчикПолученияСообщений() ПодпискаНаСообщения = Новый ОписаниеОповещения(«ОбработкаНовыхСообщений», ЭтотОбъект); СредстваТелефонии.ПодключитьОбработчикSMSСообщений(ПодпискаНаСообщения); КонецПроцедуры &НаКлиенте Процедура ОбработкаНовыхСообщений(Сообщение, ДополнительныеПараметры) //Обработка нового сообщения //Сообщение.Отправитель, Сообщение.Текст; КонецПроцедуры

Процедура «ОбработкаНовыхСообщений » будет вызываться каждый раз при получении новой SMS. Через параметр «Сообщение » передаётся объект типа «SMSСообщение » и мы без труда можем получить текст сообщения и информацию об отправителе.

Работа с MMS сообщения выполняется аналогичным образом. Сначала мы создаем SMSСообщение, а потом добавляем к нему вложение (например, изображения). Таким простым действием SMS превращается в MMS:

НовоеСообщение= Новый SMSСообщение(); Вложение = Новый MMSВложение; Вложение.Данные = Картинка; Вложение.ТипСодержимого = "image/jpeg"; MMSСообщение.Вложения.Добавить(Вложение);

Совершаем звонки из мобильного приложения

Программное совершение звонка осуществляется с помощью метода «НабратьНомер» глобального объекта «СредстваТелефонии». Перед вызовом метода крайне желательно проверить возможность совершения звонка:

Если СредстваТелефонии.ПоддерживаетсяНаборНомера() Тогда СредстваТелефонии.НабратьНомер(НомерТелефона, ВызватьСразу); КонецЕсли;

Параметр «ВызватьСразу » влияет на выполнение набора номера. Когда она равен «Истина », набор номера выполняется автоматически через стандартное приложение совершения звонков. При значении «Ложь» пользователь также увидит стандартный интерфейс приложения набора номера, но для совершения вызова потребуется нажать кнопку «Вызвать ».

Журнал звонков

Мобильная платформа позволяет разработчику взаимодействовать с журналом звонков. Например, вы без особого труда можете получить список исходящих, пропущенных или входящих звонков. Функция поддерживается только на Android:

ЖурналЗвонков = СредстваТелефонии.ПолучитьЖурналЗвонков(); Отбор = Новый ОтборКомпоновкиДанных; ЭлементОтбора = Отбор.Элементы.Добавить(Тип(«ЭлементОтбораКомпоновкиДанных»)); ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(«ТипЗвонка»); ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанныз.Равно; ЭлементОтбора.ПравоеЗначение = ТипЗвонкаЖурналаЗвонков.Пропущенный; ЭлементОтбора.Использование = Истина; СписокЗаписейЖурналаЗвонков = ЖурналЗвонков.НайтиЗаписи(Отбор); //В СписокЗаписейЖурналаЗвонков будет коллекция записей

Геопозиционирование

Практически любой современный смартфон имеет функции определения геопозиции. Этим функционалом вы можете воспользоваться из встроенного языка 1С. Получение текущих координат устройства условно можно разделить на 2 этапа: выбор провайдера геопозиционирования и обработка полученных координат:

//Предоставим выбор провайдера платформе ИдеальныйПровайдер = СредстваГеопозиционирования.ПолучитьСамогоТочногоПровайдера(); Координаты = СредстваГеопозиционирования.ПолучитьПоследнееМестоположение(ИдеальныйПровайдер); //Если координаты получали давно, то обновляем Если Координаты = Неопределено ИЛИ ТекущаяДата() – Координаты.Дата > 3600 Тогда СредстваГеопозиционирования.ОбновитьМестоположение(ИдеальныйПровайдер, 60); Координаты = СредстваГеопозиционирования.ПолучитьПоследнееМестоположение(ИдеальныйПровайдер); КонецЕсли;

Работа с мультимедийными функциями

Разработчику доступна возможность делать снимки, видеозаписи, аудиозаписи средствами встроенного языка: СделатьФотоснимок (), СделатьВидеозапись (), СделатьАудиозапись ().

Под какую мобильную ОС лучше разрабатывать на 1С?

Несмотря на мою любовь к технике Apple, создавать мобильные приложения средствами платформы 1С лучше всего под Android. Причин тут несколько, но самая главная из них – поддерживаемые функции. К сожалению, под iOS многие нужные вещи не поддерживаются. Например, отсутствие возможности ставить программную подписку на SMS сообщения или взаимодействовать с журналом звонков – могут сделать невозможным реализацию некоторых идей. Android в этом плане более дружелюбен. Не стоит также забывать о стоимости самих устройств. Не каждая компания будет готова разориться на приобретение мобильных устройств от Apple.

Вместо завершения

Платформа «1С:Предприятие 8 » на практике доказала о своей готовности стать простым инструментом для разработки корп. Приложений под мобильные платформы. Рассмотренные в статье примеры – лишние тому подтверждение. Вовсе необязательно тратить ресурсы на изучение нативных инструментов, если функционал приложения укладывается в возможности мобильной платформы и в компании доминируют продукты фирмы «1С».


Close