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

Проблема с зависимостями возникает при использовании общих пакетов или библиотек, от которых зависит несколько других пакетов, но при этом они зависят от разных и несовместимых версий общих пакетов. Работа разработчиков Android стала значительно проще благодаря Gradle: чтобы добавить в сборку необходимую библиотеку, достаточно просто добавить одну зависимость в build.gradle. Но что происходит, когда две зависимости зависят от разных версий одной и той же библиотеки?

Gradle может разрешать зависимости автоматически, если обе они принадлежат одной конфигурации, т. е. конфигурации приложения. Предположим, Android SDK для Audience Network зависит от библиотеки exoplayer. Если вы добавляете другую версию библиотеки exoplayer, Gradle выберет и добавит самую последнюю версию. Но если обе зависимости принадлежат разным конфигурациям — приложения и тестирования, — Gradle выдаст ошибку. Ниже мы рассмотрим различные конфликты и способы их решения.

Предварительные требования

Прежде чем продолжить, обязательно ознакомьтесь с руководствами по началу работы с Audience Network и Android.

Конфликты зависимости

1. Общие библиотеки в зависимости от одной и той же конфигурации

2. Общие библиотеки в зависимости от разных конфигураций

Устранение конфликтов зависимостей

1. Решение с помощью Gradle

2. Исключение определенной версии зависимости

3. Явное определение конфликтующей библиотеки в Gradle

4. Принудительное разрешение библиотеки



Конфликты зависимости

1. Общие библиотеки в зависимости от одной и той же конфигурации

Посмотрите пример ниже. Обе зависимости в коде ниже относятся к одной и той же конфигурации и имеют внутреннюю зависимость от библиотеки org.hamcrest:hamcrest-core. Поскольку обе зависимости внутренне используют разные версии одной и той же библиотеки, в сборку добавляется самая старшая версия. В выводе синхронизации Gradle будет указано, что версия библиотеки hamcrest в финальной сборке автоматически обновлена с 1.1 до 1.3.

// Depends on version 1.3 of org.hamcrest:hamcrest-core
androidTestImplementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19'  

2. Общие библиотеки в зависимости от разных конфигураций

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

При выполнении инструментальных тестов основной и тестовый APK используют один и тот же путь класса. Если основной и тестовый APK используют одну и ту же библиотеку (например, Guava), но с разными версиями, сборка Gradle завершится сбоем. Если Gradle не обнаружит этого, ваше приложение может вести себя по-разному во время тестирования и во время обычного запуска (включая сбой в одном из случаев).

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 

Устранение конфликтов зависимостей

1. Решение с помощью Gradle

Это самый простой способ, однако он ограничивается зависимостями в одинаковых конфигурациях. Например, ваш модуль явно зависит от конкретной версии библиотеки ExoPlayer. При этом SDK Audience Network уже содержит другую версию ExoPlayer. По умолчанию сборка содержит старшую версию. При использовании фрагмента кода ниже в выводе синхронизации Gradle будет явно указано, что версия библиотеки ExoPlayer в финальной сборке автоматически обновлена с r2.4.2 до 2.7.3.

implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.3'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation 'com.facebook.android:audience-network-sdk:4.28.1' 

2. Исключение определенной версии зависимости

В большинстве ситуаций разработчикам необходимо контролировать, какая версия библиотеки в итоге будет добавляться в сборку. Для этого можно настроить build.gradle. Например, если ваши разработчики предпочитают более раннюю версию ExoPlayer r2.4.0, а не r2.4.2 в SDK Audience Network, то вы можете исключить этот модуль при объявлении зависимости audience-network-sdk. Метка исключения применяется и к зависимостям в разных конфигурациях.

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

Сценарий 1. Зависимости в одной и той же конфигурации
implementation 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation ('com.facebook.android:audience-network-sdk:4.28.1') {
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-core'
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-dash'
}
Сценарий 2. Зависимости в разных конфигурациях
// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation ('org.mockito:mockito-core:1.10.19'){
    exclude group: 'org.hamcrest', module:'hamcrest-core'
}

3. Явное определение конфликтующей библиотеки в Gradle

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

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

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 
// Explictly mention that include version 1.3 of org.hamcrest:hamcrest-core
androidTestCompile 'org.hamcrest:hamcrest-core:1.3' 

4. Принудительное разрешение библиотеки

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

Этот подход следует применять с осторожностью. В первом примере, если audience-network-sdk будет обновлен и эти библиотеки обновят версию библиотек exoplayer-core и exoplayer-dash, мы всё равно будем вынуждены использовать более старую версию. Сценарий применим и для второго подхода к решению, однако в этом случае мы принудительно устанавливаем версию зависимости для всех конфигураций, а не одну конфигурацию.

Сценарий 1. Зависимости в одной и той же конфигурации
android {
    configurations.all {
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
    }
}
Сценарий 2. Зависимости в разных конфигурациях
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

Дальнейшие действия

Дополнительные ресурсы

Руководство по началу работы

Техническое руководство по началу работы с Audience Network

Справка по API

Справка по Facebook SDK для iOS