Из проекта в проект мы замечаем, что наш код, выполняет одни и те же функции и выглядит почти одинаково. Это заставляет задуматься — а не выполняем ли мы лишнюю работу, переписывая одно и то же? Мы начинаем копировать классы из предыдущих проектов и все равно понимаем, что делаем что-то не то и оказываемся правы — просто копируя классы из проекта в проект, мы запросто можем что-то потерять/заменить/затереть, а если еще наша команда ведет несколько проектов одновременно, то обнаружение ошибок в заимствованных классах потребует изменений вручную во всех проектах. Устав наступать на эти грабли, мы решаем, что нужен общий код, который будет расшариваться на все наши проекты и любые изменения в нем будут легко подтягиваться. Да, мы создаем свою библиотеку переиспользуемых компонентов! О разных способах организовать свою библиотеку, о всех плюсах и минусах подходов вы узнаете под катом :)


Существует несколько способов организовать нашу общую кодовую базу:


  1. Android библиотека (aar/jar)
  2. Git Submodule
  3. 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 варианта проверить новый код библиотеки:


  1. Создать в проекте библиотеки sample модуль, в котором будет написан код, проверяющий функционал библиотеки. Вариант простой, но есть 2 минуса: 1. Мы пишем дополнительный код; 2. Среда тестового модуля отличается от реального проекта в котором мы будем использовать библиотеку, и если мы допустим ошибки, то это всплывет уже при получении новой версии либы на проекте.
  2. Публиковать изменения в локальном репозитории mavenLocal. Благодаря этому подходу вы можете получить новый код либы в проекте, но он не будет опубликован для всей команды (но надо немного повозиться с настройкой).

Git Submodule


В предыдущем подходе мы столкнулись со сложностью получения нового кода на стадии разработки/отладки в проекте, так как код библиотеки и проекта находятся в разных репозиториях и проектах студии. Подход Git Submodule так же предполагает использование раздельных репозиториев, но позволяет в основном проекте получить библиотеку как модуль, используя Git. Это значит, что код библиотеки будет доступен в проекте и все изменения сразу доступны в проекте!


Как это работает


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



Проще говоря, у нас есть 2 репозитория: проекта и библиотеки. В репозитории проекта хранится код библиотеки и ссылка на состояние репозитория библиотеки. Так Git понимает какое состояние (версия) библиотеки нужно проекту.


Подробнее о том как работает Git Submodule тут


Как организовать командную работу


В подходе Git Submodule командная работа над библиотекой организовывается следующим образом:


  1. При создании нового проекта или подключения библиотеки к существующему проекту — создается новая Git-ветка от master с названием проекта.
  2. Когда приходит время пополнить библиотеку некоторым функционалом — создается ветка под задачу (от ветки проекта) и вносятся изменения туда.
  3. Проводится ревью, вливаются пулы в ветку проекта. Когда набирается достаточно изменений, чтобы выпустить версию — создается пул на merge ветки проекта в ветку master библиотеки.
  4. После того как пул пройдет ревью ответственной за библиотеку командой и будет влит в 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 подумайте заранее о схеме версионирования, и создайте правила/плагины для контроля ведения версий.


Всем отличного переиспользования!

Комментарии (1)


  1. agent10
    30.09.2019 09:18

    Расскажу тоже немного о своём опыте. У нас было два Андроид приложения и 3 «библиотеки» для двух приложений
    1) Вариант с мавен репозиторием считаю самым лучшим и правильным. НО только если у вас есть отдельная подкоманда, которая занимается либами, следит за версионностью и обновляет мавен. Типа «под ключ». У нас маленькая команда. У нас не получилось, отказались, перешли на submodules
    2) Но от сабмодулей тоже отказались. Да, стало удобнее, но появилась некая проблема с версиями. Плюс проблема частых изменений никуда не ушла, на любое изменение и коммит в либе надо не забывать делать менять «указатель» на коммит в приложениях. Вообщем довольно много действий на каждое телодвижение.
    3) Subtree не пробовал, но почитаю. Но мы перешли на простое монорепо, где каждая либа просто свой градл модуль, и все модули и оба приложения в одной кодовой базе. И пока в небольшой команде жизнь значительно проще