Разрешение зависимостей Gradle

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

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

Например, предположим, что ваше приложение зависит от библиотеки A и библиотеки B, которые, в свою очередь, зависят от разных версий библиотеки C.

Ваше приложение зависит от библиотеки A и библиотеки B, которые, в свою очередь, зависят от разных версий библиотеки C. Gradle выбирает новейшую версию библиотеки C.
Рисунок 1. Конфликт транзитивных версий. Gradle разрешает самую новую версию (по умолчанию).

В этом случае Gradle по умолчанию выбирает новейшую версию библиотеки C, что может вызвать проблемы с компиляцией или выполнением. В этом примере библиотека C разрешается до версии 2.1.1, но обратите внимание, что библиотека A запросила библиотеку C 1.0.3. Большая часть номера версии изменилась, что указывает на несовместимые изменения, такие как удаление функций или типов. Это может привести к сбою вызовов, сделанных из библиотеки A.

Ваше приложение может иметь прямые зависимости, которые также являются транзитивными.

Ваше приложение зависит от библиотеки A и библиотеки C. Библиотека A зависит от более новой версии библиотеки C. Gradle выбирает новейшую версию библиотеки C.
Рисунок 2. Еще один конфликт транзитивных версий. Здесь Gradle преобразуется в транзитивную версию, и ваше приложение видит эту более новую версию.

В таком случае новые транзитивные зависимости могут переопределить версию, которую вы напрямую запрашиваете в своем приложении.

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

Например, вы можете использовать задачу dependencies Gradle, запустив ./gradlew app:dependencies , чтобы отобразить дерево всех зависимостей, используемых вашим модулем приложения. Запустив это для приложения, использующего библиотеки, как показано на рисунке 2, мы увидим:

1: releaseRuntimeClasspath - Runtime classpath of /release.
2: +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0
3: |    +--- ... (omitted for brevity) ...
4: +--- com.sample:library.a:1.2.3
5: |    +--- com.sample:library.c:2.1.1
6: |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
7: |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
8: +--- com.sample:library.c:1.4.1 -> 2.1.1 (*)

В этой части отчета показаны некоторые зависимости, разрешенные для конфигурации releaseRuntimeClasspath .

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

Более подробную информацию об использовании отчетов о зависимостях Gradle можно найти в разделе Просмотр и отладка зависимостей .

Запрошенные версии можно указать напрямую, в каталоге версий или в спецификации (BOM).

Разрешение спецификации прямой версии

Указанные вами версии зависимостей становятся кандидатами на разрешение версий.

Например, чтобы запросить версию 1.7.3 библиотеки androidx.compose.ui:ui в качестве зависимости в вашем app/build.gradle.kts :

dependencies {
    implementation("androidx.compose.ui:ui:1.7.3")
}

Версия 1.7.3 становится версией-кандидатом. Gradle разрешает последнюю версию среди 1.7.3 и других версий той же библиотеки, запрошенных транзитивными зависимостями.

Разрешение каталога версий

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

Например, чтобы указать версию 1.7.3 файла androidx.compose.ui:ui в качестве зависимости в файле gradle/libs.versions.toml :

[versions]
ui = "1.7.3"

[libraries]
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }

Здесь определяется переменная с именем libs.androidx.compose.ui представляющая библиотеку. Эта версия не считается кандидатом, если вы не используете эту переменную для указания зависимости.

Чтобы запросить библиотеку и ее версию в вашем app/build.gradle.kts :

dependencies {
    implementation(libs.androidx.compose.ui)
}

Gradle решает так же, как и для прямой спецификации.

Разрешение спецификации материалов (BOM)

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

Версии спецификации влияют на ваши прямые зависимости, а также на все транзитивные зависимости, которые появляются в спецификации.

Например, укажите спецификацию в качестве зависимости платформы в вашем app/build.gradle.kts :

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.10.00"))
    implementation("androidx.compose.ui:ui")
}

Любые библиотеки, которые вы хотите использовать в качестве зависимостей, не требуют указания версии; запрошенная версия взята из спецификации.

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

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

[versions]
composeBom = "2024.10.00"

[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }

Ваше app/build.gradle.kts ссылается на спецификацию и библиотеки, используя переменные, определенные в каталоге версий:

dependencies {
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
}

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

Например, предположим, что в спецификации указаны версии библиотек A, B и C. Ваше приложение хочет напрямую использовать библиотеку A в качестве зависимости, а также библиотеку D. Библиотека D использует библиотеку B в качестве зависимости. Ничто не использует библиотеку C.

Спецификация включает версии библиотек A, B и C. Ваше приложение использует библиотеки A и D в качестве зависимостей. Библиотека D использует библиотеку B в качестве зависимости. Библиотека C не используется прямо или косвенно в этом приложении.
Рисунок 3. Сценарий спецификации.

Библиотеки A, B и D являются зависимостями приложения; библиотека C игнорируется. Gradle использует версии A и B, указанные в спецификации, в качестве кандидатов, даже если вы не указываете библиотеку B напрямую в качестве зависимости.

Если библиотека D запросила версию библиотеки B ниже 2.0.1, Gradle разрешит версию 2.0.1. Если библиотека D запросила более позднюю версию библиотеки B, Gradle разрешит эту версию.