Dev (Back & Front)ARTIGO

Trabalhando com dependências duplicadas no Gradle

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. Veja como resolver.

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:

Desenvolvedor Android focado em resolver problemas reais do dia-a-dia e buscando sempre se atualizar com as novidades do mercado móvel. Desenvolvedor de aplicativos para usuários do Google Play e também para usuário fechados para empresas. Trabalhando em empresas e também como freelancer. Marido, fã de tecnologia Google e participante ativo da maior comunidade Android do Brasil (Slack Android Dev Brasil).

Ver perfil

Mais de Rafael Araújo