Risoluzione delle dipendenze Gradle

I file di build specificano le dipendenze dirette, ma ognuna di queste può richiederne altre. Queste dipendenze transitive fanno crescere rapidamente il grafico delle dipendenze complessivo, spesso con versioni in conflitto.

Quando le parti minor (nuove funzionalità) o patch (correzioni di bug) cambiano, la libreria è ancora probabilmente compatibile e ha meno probabilità di influire sulla tua applicazione.

Ad esempio, supponiamo che la tua applicazione dipenda dalle librerie A e B, che a loro volta dipendono da versioni diverse della libreria C.

La tua app dipende dalla raccolta A e dalla raccolta B, che a loro volta dipendono da versioni diverse della raccolta C. Gradle sceglie la versione più recente della libreria C.
Figura 1. Un conflitto di versioni transitivo. Gradle si risolve nella versione più recente (per impostazione predefinita).

In questo caso, Gradle sceglie per impostazione predefinita la versione più recente della libreria C, il che potrebbe causare problemi di compilazione o di runtime. In questo esempio, la libreria C viene risolta in 2.1.1, ma tieni presente che la libreria A ha richiesto la libreria C 1.0.3. La maggior parte del numero di versione è cambiata, il che indica modifiche incompatibili, come la rimozione di funzioni o tipi. Questo potrebbe causare l'arresto anomalo delle chiamate effettuate dalla libreria A.

La tua app può avere dipendenze dirette che sono anche dipendenze transitive.

La tua app dipende dalla libreria A e dalla libreria C. La libreria A dipende da una versione più recente della libreria C. Gradle sceglie la versione più recente della libreria C.
Figura 2. Un altro conflitto di versioni transitivo. In questo caso, Gradle risolve nella versione transitiva e la tua applicazione vede la versione più recente.

In un caso come questo, le dipendenze transitive più recenti possono eseguire l'override della versione richiesta direttamente nell'app.

Gradle esamina tutte le versioni candidate per tutte le dipendenze nel grafico per determinare la versione più recente di ogni dipendenza. Puoi utilizzare le attività Gradle di base e strumenti più avanzati per determinare le versioni di ogni dipendenza risolte da Gradle. Confrontare le modifiche in questa risoluzione è fondamentale per comprendere e ridurre i rischi dell'upgrade.

Ad esempio, puoi utilizzare l'attività dependencies di Gradle eseguendo ./gradlew app:dependencies per visualizzare un albero di tutte le dipendenze utilizzate dal modulo dell'app. Se esegui questo comando su un'applicazione che utilizza le librerie come mostrato nella Figura 2,

1: releaseRuntimeClasspath - Runtime classpath of /release.
2: +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0
3: |    +--- ... (omitted for brevity) ...
4: +--- com.sample:library.a:1.2.3
5: |    +--- com.sample:library.c:2.1.1
6: |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
7: |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
8: +--- com.sample:library.c:1.4.1 -> 2.1.1 (*)

Questa parte del report mostra alcune delle dipendenze risolte per la configurazione di releaseRuntimeClasspath.

Ogni volta che vedi -> nel report sulle dipendenze, un richiedente (la tua applicazione o un'altra libreria) utilizza una versione di quella dipendenza che non è prevista. In molti casi, ciò non causa alcun problema, poiché la maggior parte delle librerie è scritta per garantire la compatibilità con le versioni precedenti. Tuttavia, alcune librerie potrebbero apportare modifiche incompatibili e questo report può aiutarti a determinare l'origine di nuovi problemi relativi al comportamento della tua applicazione.

Per ulteriori dettagli sull'utilizzo dei report sulle dipendenze di Gradle, consulta Visualizzare e debugging delle dipendenze.

Puoi specificare le versioni richieste direttamente, in un catalogo delle versioni o in una distinta dei materiali (BOM).

Risoluzione della specifica della versione diretta

Le versioni delle dipendenze specificate diventano candidate per la risoluzione della versione.

Ad esempio, per richiedere la versione 1.7.3 della libreria androidx.compose.ui:ui come dipendenza in app/build.gradle.kts:

dependencies {
    implementation("androidx.compose.ui:ui:1.7.3")
}

La versione 1.7.3 diventa una versione candidata. Gradle si risolve all'ultima versione tra la 1.7.3 e le altre versioni della stessa libreria richieste dalle dipendenze transitive.

Risoluzione del catalogo delle versioni

I cataloghi delle versioni definiscono le variabili per monitorare la versione delle dipendenze utilizzate in tutta l'applicazione. Se utilizzi una variabile del catalogo delle versioni, le dipendenze specificate della variabile vengono aggiunte alle candidate per la risoluzione della versione. Le variabili non utilizzate nel catalogo delle versioni vengono ignorate.

Ad esempio, per specificare la versione 1.7.3 di androidx.compose.ui:ui come dipendenza nel tuo file gradle/libs.versions.toml:

[versions]
ui = "1.7.3"

[libraries]
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }

Definisce una variabile denominata libs.androidx.compose.ui per rappresentare la libreria. Questa versione non è considerata candidata, a meno che non utilizzi la variabile per specificare una dipendenza.

Per richiedere la raccolta e la relativa versione in app/build.gradle.kts:

dependencies {
    implementation(libs.androidx.compose.ui)
}

Gradle si risolve allo stesso modo in cui avveniva per una specifica diretta.

Risoluzione della distinta base (BOM)

Le versioni di tutte le librerie che compaiono nel BOM diventano candidati per la risoluzione delle versioni. Tieni presente che le librerie vengono utilizzate come dipendenze solo se specificate come dirette o indirette. Le altre librerie nel BOM vengono ignorate.

Le versioni del BOM influiscono sulle dipendenze dirette e su tutte le dipendenze trasitive riportate nel BOM.

Ad esempio, specifica una BOM come dipendenza della piattaforma in app/build.gradle.kts:

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.10.00"))
    implementation("androidx.compose.ui:ui")
}

Tutte le librerie che vuoi utilizzare come dipendenze non richiedono una specifica della versione; la versione richiesta proviene dal BOM.

Tieni presente che puoi anche utilizzare un catalogo delle versioni per creare variabili per il BOM e le librerie. Ometti i numeri di versione nel catalogo delle versioni per le librerie che compaiono in una dipendenza BOM.

Ad esempio, il tuo catalogo delle versioni contiene la BOM e il relativo numero di versione, ma non specifica una versione per le librerie a cui fai riferimento dalla BOM:

[versions]
composeBom = "2024.10.00"

[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }

app/build.gradle.kts fa riferimento alla BOM e alle librerie utilizzando le variabili definite nel catalogo delle versioni:

dependencies {
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
}

La versione della libreria specificata nel BOM diventa una candidata per la risoluzione di Gradle. Inoltre, tutte le altre versioni della libreria specificate nel BOM diventano versioni candidate, indipendentemente dal fatto che le usi direttamente o meno come dipendenze.

Ad esempio, supponiamo che un BOM specifichi le versioni per le librerie A, B e C. La tua applicazione vuole utilizzare direttamente la libreria A come dipendenza, oltre alla libreria D. La libreria D utilizza la libreria B come dipendenza. Nessun elemento utilizza la libreria C.

Un BOM include le versioni per le librerie A, B e C. L'applicazione utilizza le librerie A e D come dipendenze. La libreria D utilizza la libreria B come dipendenza. La libreria C non viene utilizzata direttamente o indirettamente in questa applicazione.
Figura 3. Scenario della distinta base.

Le librerie A, B e D sono dipendenze nell'applicazione; la libreria C viene ignorata. Gradle utilizza le versioni di A e B specificate nel BOM come candidati, anche se non specifichi direttamente la libreria B come dipendenza.

Se la libreria D ha richiesto una versione della libreria B inferiore alla 2.0.1, Gradle risolve alla 2.0.1. Se la libreria D ha richiesto una versione superiore della libreria B, Gradle si risolve a quella versione.