[Из песочницы] Модульная разработка Android приложений

image


При разработке Android приложений наступают моменты, когда те или иные части кода можно вынести в виде библиотек, чтобы можно было переиспользовать их в разных проектах:


  • Модули в проекте, которые часто встречаются. Например, кастомные View
  • Когда существующий API неудобный или не позволяет сделать то, что задумали — создаем расширение для этого API


Чаще всего все проблемы были решены задолго до нас, но в нашем случае нужно было вынести часть слоя бизнес-логики и фактически весь слой, отвечающий за данные в 3 наших основных продукта объединенной компании Колёса Крыша Маркет. Все наши продукты — классифайды про автомобили, недвижимость и прочие товары. Поэтому нами, разработчиками, было решили написать одно решение для всех продуктов компании. К тому же, это облегчило нашу работу.


Попробовали git submodule


Самое простое решение — использовать git submodule. Если кратко объяснить его работу, то к git repository вы прикрепляете ссылку на другой git repository, который должен автоматически клонироваться вместе с родительским. Казалось бы все просто: у нас есть несколько git repository, где хранятся все наши модульные библиотеки и нужно всего лишь указать его в git submodule.


Но как быть с версиями? При обновлении через команду git submodule update стираются изменения в submodule, merge не происходит. Вам придется постоянно обновляться, когда другой разработчик вносит изменение в данном submodule. Вместо этого стоит разрабатывать модули в виде библиотек с jar файлом или Android Archive (aar). По факту, в этих архивах содержатся те же самые файлы, которые могли быть в git submodule, но теперь есть более понятное версионирование вместо истории commit или tag.


Осторожно, не все можно публиковать


При публикации библиотек стоить учесть, что они не должны содержать конфиденциальные данные компании. Более того, не будет удобно остальным разработчикам, если в библиотеке будет содержаться код, относящийся к конкретной компании. Следовательно, нам нужен закрытый artifactory для хранения библиотек в закрытом доступе. Есть множество open source решений, но мы используем JFrog Artifactory.


А если можно публиковать? В таком случае вас ждет светлая дорога в мир open source проектов. Достаточно просто найти открытые web серверы, которые поддерживают gradle, чтобы размещаемые jar и aar файлы подтягивались через dependency в ваших проектах.


Итак, начинаем создавать модуль


alb3cefptzpk8otjkhxtamxikeg.png
glz9fbn2yton6wjc8fhbnik7dxa.png
Начинается все просто — заходим в File > New > New Module…
Название библиотеки, которое укажете в этом окне, станет названием бинарника вашей библиотеки.


Создав модуль можете приступить к написанию кода. Если к прилагаемому коду будете писать тесты, остальным членам команды будет понятно, как вашим модулем пользоваться. Еще лучше, если создадите новый sample app module, который работает исключительно с вашим модулем для демонстрации на реальных устройствах. Удобнее, когда вам нужно предварительно протестировать код перед публикацией в artifactory. Для этого достаточно указать зависимость в build.gradle. В примерах я буду указывать librarymodule в качестве названия модуля.


# {module project}/sample-app
dependencies {
   implementation project(":librarymodule")
}


Настраиваем gradle для публикации в artifactory


Возьмем build скрипт от Chris Banes: Gradle скрипт для загрузки артифактов в Maven repositories


Конкретно в его случае описывается процесс в maven repository для публичной публикации. Основные настройки абсолютно идентичны: в каждом модуле, который нужно публиковать, вызываем gradle script файл из файла build.gradle модулей и создаем gradle.properties, где будет лежать конфигурационная информация для публикации. Нас интересует возможность публиковать в приватном artifactory


# {project}/build.gradle
RELEASE_REPOSITORY_URL={artifactory_url}/release-modules
SNAPSHOT_REPOSITORY_URL={artifactory_url}/snapshot-modules


Нам необходимо добавить 2 папки для публикации в artifactory: одну для официального релиза и snapshot для тестовых сборок модулей.


sobnhzi0dyir5lwnsziys3crhnk.png
Когда у нас наконец-то настроен конфигурационный файл gradle.properties, мы готовы сделать первую публикацию библиотек в artifactory. В тестовой сборке укажем SNAPSHOT в VERSION_NAME. Затем выполним команду:


./gradlew clean build sdk:uploadArchives


tbk_hre334nx6b-vx8rqzteqiia.png


Настройка CI окружения


Нам удалось вручную опубликовать модули в наш приватный artifactory. В завершениb осталось настроить CI окружение, чтобы мы могли встроить публикацию в наш процесс разработки.
Мы хотим, чтобы только CI мог публиковать в release artifactory, а для остальных добавим свободный доступ на чтение и запись в snapshot. Поэтому внесем изменения в корневом файле gradle.properties, который обычно лежит в ~/.gradle/gradle.properties. Пропишем {%username%} и {%password%} для доступа при публикации в artifactory. Таким образом у нас будет настроен уровень доступа, без внесения изменений в .gitignore.


// ~/.gradle/gradle.properties
artifactory_username=login
artifactory_password=password


Добавляем trigger на ветку master, чтобы при merge у нас собралась сборка для публикации в release artifactory. В список задач сборок добавляем запуск тестов, если у нас в модуле они есть. Хорошо будет ещё настроить auto increment версии.


# Checkout git repository with modules
# Запускаем тест
./gradlew test
# Собираем архив для публикации
./gradlew librarymodule:build
# Публикация в artifactory
./gradlew libarrymodule:uploadArchives


Используем модуль в продукте


Как только CI соберет сборку и зальет ее в artifactory, в проекте можно добавить зависимость этого модуля. Но погодите, нам же нужно добавить еще и url к нашему artifactory, ведь они недоступны в типичном repository как maven


# {project}/build.gradle
allprojects {
    repositories {
        maven { url artifactory_url/release-modules }
        maven { url {artifactory_url}/snapshot-modules }
   }
}


Теперь мы можем использовать его в проекте.


# {project}/app/build.gradle
dependencies {
   implementation ‘kz.kolesa-team: librarymodule:1.0.0’
}


С чем мы столкнулись


При подключении Kotlin в наши модули документация для Kotlin файлов не подтягивается. Оказалось, что для этого нужно добавить gradle задачу с Dokka.


Если несколько разработчиков указывали идентичные версии для библиотек при публикации, то, естественно, актуальной версией модели оказывалась последняя публикация. В процессе разработки модуля обязали указывать постфикс с идентификатором ticket из Jira. Получается что-то вроде 1.0.1-AAS-1-SNAPSHOT.


Что в итоге получилось


Со временем у нас возросло количество модулей. Мы держали каждый модуль в отдельном git repository. Множество git repository создает большое количество неудобств в процессе разработки, если модуль имеет зависимость от других модулей. В итоге получилось, что для выполнения одной задачи, необходимо было создавать PR в каждом git repository. Не совсем ясно, в каком порядке их нужно тестировать, не учитывая еще постоянное изменение PR, если в одном из PR внесли правки. Поэтому, использовать один git repository для всех модулей на практике оказалось удобнее всего.


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


Такая модульная разработка внутренних компонентов бизнеса поможет нам повысить скорость доставки фичи до пользователей. В заключении стоит отметить важное преимущество модульной разработки — сборка apk файлов будет занимать намного меньше времени, так как gradle будет пересобирать только ту часть модуля, в которой произошло изменение.

© Habrahabr.ru