Resolución de dependencias de SDK en Android

El problema de las dependencias tiene que ver con bibliotecas o paquetes compartidos en los cuales otros paquetes tienen dependencias, pero dependen de versiones diferentes e incompatibles respecto de los paquetes compartidos. Gradle ha facilitado bastante las cosas para los desarrolladores de Android: solo se debe agregar una dependencia en build.gradle y la biblioteca requerida se incluirá sin complicaciones en la compilación. ¿Pero qué sucede cuando dos dependencias tienen una dependencia en diferentes versiones de la misma biblioteca?

Gradle puede resolver de forma automática dos dependencias si ambas pertenecen a la misma configuración; es decir, a la configuración de la app. Por ejemplo, el Android SDK de Audience Network Android depende de la biblioteca "exoplayer". Si tienes que agregar una versión diferente de la biblioteca "exoplayer", Gradle seleccionará la versión superior para su inclusión. Sin embargo, si ambas dependencias pertenecen a diferentes configuraciones (es decir, app y prueba), se generará un error en Gradle. A continuación, revisaremos los diferentes conflictos y las soluciones.

Requisitos previos

Asegúrate de completar las guías de primeros pasos para Audience Network y Android antes de continuar.

Conflictos de dependencias

1: Bibliotecas compartidas en dependencias de la misma configuración

1: Bibliotecas compartidas en dependencias de diferentes configuraciones

Soluciones de conflictos de dependencias

1: Dejar que Gradle resuelva las dependencias

2: Excluir una versión específica de la dependencia

3: Definir de forma explícita la biblioteca conflictiva en Gradle

4: Forzar la resolución de la biblioteca



Conflictos de dependencias

1: Bibliotecas compartidas en dependencias de la misma configuración

Considera el siguiente ejemplo: ambas dependencias del código siguiente permanecen en la misma configuración y tienen una dependencia interna de una biblioteca "org.hamcrest:hamcrest-core". Debido a que ambas dependencias usan de manera interna diferentes versiones de la misma biblioteca, se incluye la versión más alta en la compilación. El resultado de la sincronización de Gradle indicará claramente que actualiza de forma automática la biblioteca hamcrest de la versión 1.1 a la 1.3 en la compilación final.

// 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'  

1: Bibliotecas compartidas en dependencias de diferentes configuraciones

Si ambas dependencias pertenecen a diferentes configuraciones (es decir, app y prueba), se generará un error en Gradle. Considera el siguiente fragmento de código. La primera dependencia pertenece a la configuración de la app, mientras que la segunda pertenece a la configuración de prueba. Por lo tanto, al compilar el proyecto se producirá un error con excepción.

Cuando se ejecutan pruebas de instrumentación, el APK principal y el APK de prueba comparten la misma ruta de clase. La compilación de Gradle generará un error si el APK principal y el APK de prueba usan la misma biblioteca (p. ej., Guava) en diferentes versiones. Si Gradle no lo detecta, tu app podría comportarse de manera diferente durante las pruebas y durante la ejecución normal (incluso con bloqueo en uno de los casos).

// 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' 

Soluciones de conflictos de dependencias

1: Dejar que Gradle resuelva las dependencias

Este enfoque es el más simple, pero está limitado a dependencias en las mismas configuraciones. Por ejemplo, tu módulo depende de forma explícita de una versión específica de ExoPlayer. Sin embargo, el SDK de Audience Network ya incorpora otra versión de ExoPlayer. Por defecto, la versión más alta se incluye en la compilación. Con el fragmento de código siguiente, la salida de la sincronización de Gradle indicará claramente que actualiza de forma automática la biblioteca ExoPlayer de la versión r2.4.2 a la 2.7.3 en la compilación final.

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: Excluir una versión específica de una dependencia

En la mayoría de las situaciones, es posible que los desarrolladores quieran controlar la versión de la biblioteca que finalmente se incluirá en la compilación. Es posible configurar build.gradle de modo que esto sea posible. Por ejemplo, si tu desarrollador prefiere la versión anterior de ExoPlayer (la r2.4.0) en lugar de la r2.4.2 en el SDK de Audience Network, puedes excluir ese módulo al declarar la dependencia "audience-network-sdk". Además, la etiqueta "exclude" se aplica a dependencias de diferentes configuraciones.

En un proyecto real, habrá muchas dependencias que tendrán diferentes versiones de la misma biblioteca. En ese caso, deberás contar con la etiqueta "exclude" para cada dependencia a fin de incluir la versión prevista de dicha biblioteca.

Situación 1: Dependencias en la misma configuración
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'
}
Situación 2: Dependencias en una configuración diferente
// 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: Definir de forma explícita la biblioteca conflictiva en Gradle

Es una manera más limpia de resolver el conflicto en el caso de dependencias de diferentes configuraciones. En este caso, debemos mencionar de forma explícita la versión de la biblioteca que queremos incluir en la compilación final para cualquiera de las configuraciones.

Este enfoque para la resolución de los conflictos es más limpio, pero su desventaja es que, al actualizar las dependencias reales como junit y mockito, el desarrollador también debe actualizar la biblioteca conflictiva.

// 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: Forzar la resolución de la biblioteca

Esta es otra opción para resolver el conflicto: en lugar de hacer la declaración para una configuración, la forzamos para todas las configuraciones. En el siguiente ejemplo, puedes agregar tu propia "resolutionStrategy" al nivel de módulo build.gradle, para que aplique la versión especificada del paquete que se incluirá independientemente de las dependencias en la misma configuración o en configuraciones diferentes.

Este enfoque se debe implementar con precaución. En nuestro primer ejemplo, si audience-network-sdk se actualiza y estas bibliotecas actualizan la versión de las bibliotecas exoplayer-core y exoplayer-dash, podríamos forzar el uso de una versión anterior. Aunque la situación se cumple para la segunda propuesta de solución, con este método forzamos la versión de la dependencia en todas las configuraciones en lugar de hacerlo en una sola configuración.

Situación 1: dependencias en la misma configuración
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'
    }
}
Situación 2: dependencias en una configuración diferente
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

Próximos pasos

Más recursos

Guía introductoria

Guía técnica para comenzar a usar Audience Network

Referencia de la API

Referencia del SDK de Facebook para iOS