Android

30 mar, 2017

Trabalhando com dependências duplicadas no Gradle

Publicidade

Dependendo do projeto, as dependências do Gradle podem ser um problema para o desenvolvedor. Isso acontece quando algumas bibliotecas que importamos no projeto usam outras bibliotecas dentro delas em versões diferentes. Quando isso acontece, o Gradle exibe a mensagem de Conflict with dependency. Para resolver isso, temos algumas opções.

Exemplo

Para exemplificar, será usado um cenário onde o projeto compila a biblioteca de Suporte v7 e compila a biblioteca de testes Espresso.

dependencies {
    compile 'com.android.support:appcompat-v7:25.1.0'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

Quando tentamos sincronizar o projeto, o Android Studio irá avisar da duplicidade da biblioteca de Suporte de Anotações, pois ambas bibliotecas importadas a utilizam, porém em versões diferentes (a Suporte V7 usa a versão 25.1.0 e a Espresso usa a versão 23.1.1).

Erro do Android Studio

Analisando duplicidades

Linha de comando

Execute o comando no arquivo gradlew localizado na raiz do projeto:

  • ./gradlew app:dependencies (Linux e Mac OS X)
  • gradlew app:dependencies (Windows, usando gradlew.bat)

(Onde app: é o modulo).

O comando irá exibir a lista de dependências de cada biblioteca usada no projeto. Abaixo está o que há por dentro da biblioteca do Espresso e da Suporte V7, respectivamente:

\--- com.android.support.test.espresso:espresso-core:2.2.2
     +--- com.squareup:javawriter:2.1.1
     +--- com.android.support.test:rules:0.5
     |    \--- com.android.support.test:runner:0.5
     |         +--- com.android.support:support-annotations:23.1.1
     |         +--- junit:junit:4.12
     |         |    \--- org.hamcrest:hamcrest-core:1.3
     |         \--- com.android.support.test:exposed-instrumentation-api-publish:0.5
     +--- com.android.support.test:runner:0.5 (*)
     +--- javax.inject:javax.inject:1
     +--- org.hamcrest:hamcrest-library:1.3
     |    \--- org.hamcrest:hamcrest-core:1.3
     +--- com.android.support.test.espresso:espresso-idling-resource:2.2.2
     +--- org.hamcrest:hamcrest-integration:1.3
     |    \--- org.hamcrest:hamcrest-library:1.3 (*)
     +--- com.google.code.findbugs:jsr305:2.0.1
     \--- javax.annotation:javax.annotation-api:1.2
\--- com.android.support:appcompat-v7:25.1.0
     +--- com.android.support:support-annotations:25.1.0
     +--- com.android.support:support-v4:25.1.0
     |    +--- com.android.support:support-compat:25.1.0
     |    |    \--- com.android.support:support-annotations:25.1.0
     |    +--- com.android.support:support-media-compat:25.1.0
     |    |    +--- com.android.support:support-annotations:25.1.0
     |    |    \--- com.android.support:support-compat:25.1.0 (*)
     |    +--- com.android.support:support-core-utils:25.1.0
     |    |    +--- com.android.support:support-annotations:25.1.0
     |    |    \--- com.android.support:support-compat:25.1.0 (*)
     |    +--- com.android.support:support-core-ui:25.1.0
     |    |    +--- com.android.support:support-annotations:25.1.0
     |    |    \--- com.android.support:support-compat:25.1.0 (*)
     |    \--- com.android.support:support-fragment:25.1.0
     |         +--- com.android.support:support-compat:25.1.0 (*)
     |         +--- com.android.support:support-media-compat:25.1.0 (*)
     |         +--- com.android.support:support-core-ui:25.1.0 (*)
     |         \--- com.android.support:support-core-utils:25.1.0 (*)
     +--- com.android.support:support-vector-drawable:25.1.0
     |    +--- com.android.support:support-annotations:25.1.0
     |    \--- com.android.support:support-compat:25.1.0 (*)
     \--- com.android.support:animated-vector-drawable:25.1.0
          \--- com.android.support:support-vector-drawable:25.1.0 (*)

Ambas bibliotecas estão utilizando a com.android.support:support porém com versões diferentes (destacado em negrito).

Plugin do relatório de projeto

Para algo mais “visual”, o Gradle oferece um plugin para gerar o mesmo relatório acima via página HTML, com opção de expandir e recolher as dependências internas para melhor navegação.

Para isso, é necessário configurar o plugin no build.gradle do seu modulo (geralmente, app/build.gradle).

apply plugin: 'com.android.application'
apply plugin: 'project-report'

Depois de tentar sincronizar, será possível rodar o comando:

  • ./gradlew htmlDependencyReportTask (Linux e Mac OS X)
  • gradlew htmlDependencyReportTask (Windows, usando gradlew.bat)

Feito isso, você poderá abrir o relatório pelo seu Navegador. Ele está em:
{Seu Projeto}/app/build/reports/project/dependencies/index.html

Plugin Android Studio

Também existe um plugin para o Android Studio que permite ver a arvore de dependência dentro da IDE. Ele se chama Gradle View e para instalá-lo no Android Studio basta ir em File > Settings. Na sessão de Plugins, clique em Browser repositories…. Na nova janela, buscar por Gradle View e clicar em Install.

Instalação do plugin

Reinicie o Android Studio e agora aparecerá esta aba na parte inferior direita:

Plugin do Android Android para analisar dependências

Obrigado especial ao Ray Holder pela excelente biblioteca.

Resolvendo o problema

Ambas soluções propostas serão feitas no mesmo lugar em que se declara as dependências.

Exclude

Com a tag exclude, configuramos para o Gradle excluir a dependência interna desejada. Podemos identificá-la por grupo (tag exclude group), ou por nome (tag exclude module) ou por ambos.

dependencies {
    compile 'com.android.support:appcompat-v7:25.1.0'
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
        exclude group: 'com.android.support'
        exclude module: 'support-annotations'
        exclude group: 'com.android.support', module: 'support-annotations'
    }

Assim é configurado que a biblioteca não irá compilar a dependência citada no exclude e utilizará outra encontrada dentro do projeto como um todo.

Force

Outra opção seria forçar o projeto todo a usar uma versão especifica:

configurations.all {
    resolutionStrategy {
        force 'com.android.support:support-annotations:25.1.0'
    }
}

Conclusão

A solução mais sensata é remover as dependências internas mais desatualizadas do projeto (usando exclude).

A desvantagem de forçar uma versão de uma biblioteca no projeto (usando force) é que se alguma biblioteca for atualizada e sua dependência interna também for atualizada, ela continuará a utilizar a versão fixada no force mesmo assim. Por exemplo, se biblioteca de Suporte v7 for atualizada e a biblioteca Suporte de Anotações utilizada internamente também for atualizada, ela continuará usando a versão fixada no force, podendo causar outros problemas, como ausência de funcionalidades que garantem o bom funcionamento da V7.

E, consequentemente, excluindo a dependência mais desatualizada, garantimos que a dependência será atualizada automaticamente caso a biblioteca atualize.

Considerações finais

Documentação oficial

Na documentação do Gradle há uma parte falando sobre listar as depedências, utilizar plugin de relatório, excluir dependências e resolução de conflitos.

Comunidade Android

Participe do maior forum sobre Android no Brasil no slack Android Dev BR: