Resolución de dependencias del SDK en Android

El problema de la dependencia se produce con bibliotecas o paquetes compartidos con los que otros paquetes tienen dependencias, pero dependen de versiones diferentes e incompatibles de los paquetes compartidos. Gradle ha facilitado mucho la vida a los desarrolladores de Android. Ahora solo tienes que añadir una dependencia en build.gradle y la biblioteca necesaria se incluirá fácilmente en la compilación. Pero, ¿qué pasa cuando dos dependencias tienen una dependencia de versiones distintas de la misma biblioteca?

Gradle puede resolver dependencias automáticamente si ambas dependencias pertenecen a la misma configuración; por ejemplo, la configuración de la aplicación. Por ejemplo, el SDK de Audience Network para Android depende de una biblioteca "exoplayer". Si necesitas añadir una versión distinta de una biblioteca "exoplayer", Gradle elegirá la versión superior para incluirla. Sin embargo, si ambas dependencias pertenecen a configuraciones distintas (p. ej., de aplicación y prueba), Gradle devolverá un error. A continuación, se describen los distintos conflictos y sus soluciones.

Requisitos previos

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

Conflictos de dependencia

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

2: Bibliotecas compartidas en dependencias con configuraciones distintas

Soluciones a conflictos de dependencia

1: Dejar que Gradle resuelva las dependencias automáticamente

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

3: Definir explícitamente la biblioteca que genera conflicto en Gradle

4: Forzar la resolución de la biblioteca



Conflictos de dependencia

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

Considera el ejemplo siguiente. Las dos dependencias del código siguiente tienen la misma configuración y una dependencia interna de una biblioteca "org.hamcrest:hamcrest-core". Ambas dependencias utilizan distintas versiones de la misma biblioteca internamente y se incluye la versión superior en la compilación. El resultado de la sincronización de Gradle indicará claramente que actualiza automáticamente la versión de la biblioteca hamcrest de 1.1 a 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'  

2: Bibliotecas compartidas en dependencias con configuraciones distintas

Si ambas dependencias pertenecen a configuraciones distintas (p. ej., de aplicación y prueba), Gradle devolverá un error. Considera el fragmento de código siguiente. La primera dependencia pertenece a la configuración de la aplicación, mientras que la segunda pertenece a la configuración de prueba. Al compilar el proyecto, este devolverá una excepción.

Cuando se realizan pruebas de instrumentación, tanto el APK principal como el APK de prueba comparten la misma ruta de clase. La compilación de Gradle devolverá un error si el APK principal y el APK de prueba utilizan la misma biblioteca (p. ej., Guava), pero en versiones distintas. Si Gradle no lo tuvo en cuenta, la aplicación podría tener un comportamiento distinto durante las pruebas y durante la ejecución normal (incluido el 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 a conflictos de dependencia

1: Dejar que Gradle lo resuelva automáticamente

Este enfoque es el más sencillo, pero está limitado a las dependencias que comparten la misma configuración. Pongamos por ejemplo que tu módulo depende explícitamente de una versión concreta de ExoPlayer. Sin embargo, el SDK de Audience Network ya ha incluido otra versión de ExoPlayer. De forma predeterminada, se incluye la versión superior en la compilación. Con el fragmento de código siguiente, el resultado de la sincronización de Gradle indicará claramente que actualiza automáticamente la versión de la biblioteca ExoPlayer de r2.4.2 a 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 la versión específica de la dependencia

En la mayoría de situaciones, los desarrolladores quieren tener el control sobre la versión de la biblioteca que se incluirá finalmente en la compilación. Puedes configurar build.gradle para que esto sea posible. Por ejemplo, si tu desarrollador prefiere la versión inferior de ExoPlayer r2.4.0, en lugar de la versión r2.4.2, en el SDK de Audience Network, puedes excluir dicho módulo y declarar la dependencia "audience-network-sdk". La etiqueta exclude también se aplica a dependencias con distintas configuraciones.

En un proyecto real, habrá muchas dependencias con versiones distintas de la misma biblioteca. En ese caso, para cada dependencia, necesitarás la etiqueta exclude para poder incluir la versión que esperas de la biblioteca.

Scenario 1: Dependencies in Same Configuration
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'
}
Scenario 2: Dependencies in Different Configuration
// 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 explícitamente la biblioteca que genera conflicto en Gradle

Esta es una forma más rápida de resolver el conflicto para las dependencias de distintas configuraciones. En este caso, necesitamos mencionar explícitamente la versión de la biblioteca que queremos incluir en la compilación final para cualquiera de las configuraciones.

Este es un método más rápido para resolver los conflictos, pero tiene un inconveniente: al actualizar las dependencias reales (p. ej., junit y mockito), el desarrollador también necesita actualizar la biblioteca en conflicto.

// 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 forma de resolver el conflicto: en lugar de elegir una configuración, lo forzamos para todas las configuraciones. En el ejemplo siguiente, puedes añadir tu propio valor de "resolutionStrategy" al nivel de módulo build.gradle para que fuerce la inclusión de la versión especificada del paquete, independientemente de si las dependencias tienen distintas configuraciones o la misma.

Este enfoque se debe utilizar 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, forzaríamos igualmente el uso de una versión anterior. Aunque el escenario tiene un valor verdadero para el enfoque de la segunda solución, en este enfoque se fuerza la versión de dependencia en todas las configuraciones, en lugar de en una única configuración.

Scenario 1: Dependencies in Same Configuration
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'
    }
}
Scenario 2: Dependencies in Different Configuration
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

Siguientes pasos

Más recursos

Guía de introducción

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

Referencia de la API

Referencia del SDK de Facebook para iOS