Из проекта в проект мы замечаем, что наш код, выполняет одни и те же функции и выглядит почти одинаково. Это заставляет задуматься — а не выполняем ли мы лишнюю работу, переписывая одно и то же? Мы начинаем копировать классы из предыдущих проектов и все равно понимаем, что делаем что-то не то и оказываемся правы — просто копируя классы из проекта в проект, мы запросто можем что-то потерять/заменить/затереть, а если еще наша команда ведет несколько проектов одновременно, то обнаружение ошибок в заимствованных классах потребует изменений вручную во всех проектах. Устав наступать на эти грабли, мы решаем, что нужен общий код, который будет расшариваться на все наши проекты и любые изменения в нем будут легко подтягиваться. Да, мы создаем свою библиотеку переиспользуемых компонентов! О разных способах организовать свою библиотеку, о всех плюсах и минусах подходов вы узнаете под катом :)
Существует несколько способов организовать нашу общую кодовую базу:
- Android библиотека (aar/jar)
- Git Submodule
- Git Subtree
Android библиотека (aar/jar)
Любая библиотека для нашего приложения — это просто множество классов, организованных определенным образом. Каждый раз, когда мы в build.gradle подключаем какой-нибудь Retrofit или Dagger — мы загружаем библиотеку в виде aar/jar архива с одной из платформ публикации библиотек. Самыми популярными платформами публикации библиотек считают JCenter и MavenCentral. Разработчики библиотеки работают в своем репозитори над новой версией, и когда версия становится готова для выхода в мир — они публикуют ее на одной из платформ и говорят "Хей, мы выпустили новую версию нашей топовой библиотеки!". Все что остается сделать разработчикам, которые используют эту либу в своих проектах — поменять версию в build.gradle и радоваться новым фичам. Удобно? Не то слово!
Но насколько удобен такой подход, если наша библиотека развивается и каждый день пополняется новыми фичами разными разработчиками с разных проектов нашей команды? Давайте посмотрим как это выглядит на практике.
Мы создаем репозиторий нашей библиотеки, вносим туда какие-то фичи, отлаживаем, и готовы поделиться ей с нашей командой. Тогда мы узнаем о таких словах как JCenter, MavenCentral, Bintray, Jitpack.io… все это платформы для публикации библиотек. Сейчас основной платформой для Android проектов считается JCenter. Если вы создадите проект, то увидете, что в build.gradle (уровня проекта) в repositories указан JCenter
repositories {
google()
jcenter()
}
То есть если разработчик захочет подключить вашу библиотеку, то ему достаточно будет подключить ее в build.gradle уровня модуля.
Самым простым способом публикации библиотеки для меня кажется Jitpack.io, пара действий и ваша либа готова к использованию.
Как организовать командную работу над библиотекой
Если мы создали библиотеку и залили ее на репозиторий, то у остальной нашей команды есть только полученный jar/aar архив. Для того, чтобы вся команда могла вести работу над либой — каждый разработчик должен выкачать себе репозиторий библиотеки и вносить изменения в нем.
Версионирование
При разработке и использовании библиотек приходится сталкиваться с таким понятием как версионирование. То есть набор изменений в библиотеке, который мы хотим опубликовать, должен фиксироваться версией. Это поможет при обновлении библиотеки на новую версию понимать насколько серьезные/ломающие изменения были внесены, благодаря принятой схеме версионирования.
Проверка библиотеки в проекте
Для того, чтобы проверить, что внесенные изменения выполняют то, что мы задумывали — необходимо проверить поведение написанного кода в проекте. Поднимаем версию библиотеки и… тут и находится одно из узких мест этого подхода. Наша библиотека и проект находятся в разных репозиториях, это значит, что мы не можем просто так получить классы библиотеки в проекте. У нас есть 2 варианта проверить новый код библиотеки:
- Создать в проекте библиотеки sample модуль, в котором будет написан код, проверяющий функционал библиотеки. Вариант простой, но есть 2 минуса: 1. Мы пишем дополнительный код; 2. Среда тестового модуля отличается от реального проекта в котором мы будем использовать библиотеку, и если мы допустим ошибки, то это всплывет уже при получении новой версии либы на проекте.
- Публиковать изменения в локальном репозитории mavenLocal. Благодаря этому подходу вы можете получить новый код либы в проекте, но он не будет опубликован для всей команды (но надо немного повозиться с настройкой).
Git Submodule
В предыдущем подходе мы столкнулись со сложностью получения нового кода на стадии разработки/отладки в проекте, так как код библиотеки и проекта находятся в разных репозиториях и проектах студии. Подход Git Submodule так же предполагает использование раздельных репозиториев, но позволяет в основном проекте получить библиотеку как модуль, используя Git. Это значит, что код библиотеки будет доступен в проекте и все изменения сразу доступны в проекте!
Как это работает
Подмодули позволяют содержать один Git-репозиторий как подкаталог другого Git-репозитория. Это даёт возможность клонировать ещё один репозиторий внутрь проекта, храня коммиты для этого репозитория отдельно.
Проще говоря, у нас есть 2 репозитория: проекта и библиотеки. В репозитории проекта хранится код библиотеки и ссылка на состояние репозитория библиотеки. Так Git понимает какое состояние (версия) библиотеки нужно проекту.
Подробнее о том как работает Git Submodule тут
Как организовать командную работу
В подходе Git Submodule командная работа над библиотекой организовывается следующим образом:
- При создании нового проекта или подключения библиотеки к существующему проекту — создается новая Git-ветка от master с названием проекта.
- Когда приходит время пополнить библиотеку некоторым функционалом — создается ветка под задачу (от ветки проекта) и вносятся изменения туда.
- Проводится ревью, вливаются пулы в ветку проекта. Когда набирается достаточно изменений, чтобы выпустить версию — создается пул на merge ветки проекта в ветку master библиотеки.
- После того как пул пройдет ревью ответственной за библиотеку командой и будет влит в master ветку — остальные команды проектов будут уведомлены о появившемся обновлении библиотеки и примут решение об обновлении.
Версионирование
Когда пул был влит в master, и командам приходит уведомление об обновлении библиотеки, они не в курсе насколько глобальны изменения в новой версии. Ведь подход с Git Submodule не требует никакой схемы версионирования. Но эта проблема легко решается введением схемы версионирования. Все что требуется — писать версию и описание того, что было изменено и добавлено в описании к пул реквесту в master ветку. Тогда разработчики будут понимать насколько им сейчас реально обновиться на новую версию библиотеки. Звучит здорово но возникает вопрос:
Да, студия не умеет коммитить в отдельно в либу, подключенную сабмодулем. Я для решения этой проблемы использую SourceTree. Это приложение есть для Windows и Mac, а для Linux есть GitKraken.
Git Subtree
Git Subtree является усовершенствованной версией Git Submodule. В Git Subtree постарались решить проблемы, с которыми разработчики сталкивались во время работы с Git Submodule, на хабре есть хорошая статья с описанием различий инструментов. Хотя они и работают по-другому но решают одну задачу.
Заключение
Инструменты Git Submodule/Subtree отлично подходят для решения задачи создания общей кодовой базы команды, занимающейся несколькими проектами. Одним из важных преимуществ является моментальная проверка нового кода на проекте после внесения изменений в библиотеке. В этом стандартный подход с публикацией библиотеки на JCenter или MavenCentral уступает. Если вы решите взять к себе в команду Git Submodule/Subtree подумайте заранее о схеме версионирования, и создайте правила/плагины для контроля ведения версий.
Всем отличного переиспользования!
agent10
Расскажу тоже немного о своём опыте. У нас было два Андроид приложения и 3 «библиотеки» для двух приложений
1) Вариант с мавен репозиторием считаю самым лучшим и правильным. НО только если у вас есть отдельная подкоманда, которая занимается либами, следит за версионностью и обновляет мавен. Типа «под ключ». У нас маленькая команда. У нас не получилось, отказались, перешли на submodules
2) Но от сабмодулей тоже отказались. Да, стало удобнее, но появилась некая проблема с версиями. Плюс проблема частых изменений никуда не ушла, на любое изменение и коммит в либе надо не забывать делать менять «указатель» на коммит в приложениях. Вообщем довольно много действий на каждое телодвижение.
3) Subtree не пробовал, но почитаю. Но мы перешли на простое монорепо, где каждая либа просто свой градл модуль, и все модули и оба приложения в одной кодовой базе. И пока в небольшой команде жизнь значительно проще