Отладка ошибок разрешения зависимостей

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

Рекомендации по исправлению ошибок разрешения зависимостей, связанных с настраиваемой логикой сборки, см. в разделе Стратегии настраиваемого разрешения зависимостей .

Просмотр зависимостей модуля

Некоторые прямые зависимости могут иметь собственные зависимости. Это так называемые транзитивные зависимости . Вместо того, чтобы требовать от вас вручную объявлять каждую транзитивную зависимость, Gradle автоматически собирает и добавляет их за вас. Плагин Android для Gradle предоставляет задачу, которая отображает список зависимостей, которые Gradle разрешает для данного модуля.

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

debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...

debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...

Для запуска задачи выполните следующие действия:

  1. Выберите «Просмотр» > «Окна инструментов» > Gradle (или нажмите Gradle в панели инструментов).
  2. Разверните AppName > Задачи > android и дважды щелкните androidDependency . После того, как Gradle выполнит задачу, должно открыться окно «Выполнить» для отображения результатов.

Дополнительные сведения об управлении зависимостями в Gradle см. в разделе «Основы управления зависимостями» в Руководстве пользователя Gradle.

Исключить транзитивные зависимости

По мере увеличения объема приложения оно может содержать ряд зависимостей, включая прямые зависимости и транзитивные зависимости (библиотеки, от которых зависят импортированные библиотеки вашего приложения). Чтобы исключить транзитивные зависимости, которые вам больше не нужны, вы можете использовать ключевое слово exclude , как показано ниже:

Котлин

dependencies {
    implementation("some-library") {
        exclude(group = "com.example.imgtools", module = "native")
    }
}

классный

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

Исключить транзитивные зависимости из тестовых конфигураций

Если вам необходимо исключить определенные транзитивные зависимости из ваших тестов, приведенный выше пример кода может работать не так, как ожидалось. Это связано с тем, что тестовая конфигурация (например, androidTestImplementation ) расширяет конфигурацию implementation модуля. То есть он всегда содержит зависимости implementation , когда Gradle разрешает конфигурацию.

Итак, чтобы исключить транзитивные зависимости из ваших тестов, вы должны сделать это во время выполнения, как показано ниже:

Котлин

android.testVariants.all {
    compileConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
    runtimeConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
}

классный

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

Примечание. Вы по-прежнему можете использовать ключевое слово exclude в блоке зависимостей, как показано в исходном примере кода из раздела «Исключение транзитивных зависимостей» , чтобы исключить транзитивные зависимости, специфичные для тестовой конфигурации и не включенные в другие конфигурации.

Исправить ошибки разрешения зависимостей

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

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

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

  1. Выберите «Навигация» > «Класс» в строке меню.
  2. Во всплывающем диалоговом окне поиска убедитесь, что установлен флажок Включить элементы, не относящиеся к проекту .
  3. Введите имя класса, которое появляется в ошибке сборки.
  4. Проверьте результаты на наличие зависимостей, включающих этот класс.

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

Исправьте повторяющиеся ошибки класса

Если класс появляется в пути к классам во время выполнения более одного раза, вы получаете сообщение об ошибке, подобное следующему:

Program type already present com.example.MyClass

Эта ошибка обычно возникает из-за одного из следующих обстоятельств:

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

Исправить конфликты между путями к классам

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

Путь к классам среды выполнения вашего приложения также определяет номера версий, которые требуются Gradle для сопоставления зависимостей в пути к классам среды выполнения для тестового APK приложения. Иерархия путей к классам описана на рисунке 1.

Рисунок 1. Номера версий зависимостей, которые появляются в нескольких путях к классам, должны совпадать в соответствии с этой иерархией.

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

При разрешении зависимостей от путей к классам среды выполнения и компиляции плагин Android Gradle 3.3.0 и выше пытается автоматически исправить определенные конфликты последующих версий. Например, если путь к классам среды выполнения включает библиотеку A версии 2.0, а путь к классам компиляции включает библиотеку A версии 1.0, плагин автоматически обновляет зависимость от пути к классам компиляции до библиотеки A версии 2.0, чтобы избежать ошибок.

Однако если путь к классам среды выполнения включает библиотеку A версии 1.0, а путь к классам компиляции включает библиотеку A версии 2.0, подключаемый модуль не понижает зависимость от пути к классам компиляции до библиотеки A версии 1.0, и вы все равно получаете ошибку, подобную следующей:

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

Чтобы решить эту проблему, выполните одно из следующих действий:

  • Включите нужную версию зависимости в качестве зависимости api к вашему библиотечному модулю. То есть только ваш библиотечный модуль объявляет зависимость, но модуль приложения также будет транзитивно иметь доступ к своему API.
  • Альтернативно вы можете объявить зависимость в обоих модулях, но вам следует убедиться, что каждый модуль использует одну и ту же версию зависимости. Рассмотрите возможность настройки свойств всего проекта , чтобы гарантировать, что версии каждой зависимости остаются согласованными на протяжении всего проекта.