Gestire le dipendenze tra moduli
Mantenga il grafo pulito e aciclico
Gestire le dipendenze tra moduli è una lezione Android Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Android Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Android Academy include 4 lezioni in totale.
Il grafo delle dipendenze
Ogni modulo dichiara da quali altri moduli dipende. Nel loro insieme, queste dipendenze formano un grafo delle dipendenze. Un grafo sano è un DAG (grafo diretto aciclico): le dipendenze puntano in una sola direzione e non formano mai cicli.
In questa lezione imparerà a dichiarare le dipendenze in modo pulito, a scegliere la configurazione Gradle corretta, a condividere le versioni e a prevenire i cicli.
Dichiarare una dipendenza tra moduli
Per aggiungere una dipendenza a un altro modulo, utilizzi project(":path:to:module") all'interno del blocco dependencies. Il percorso rispecchia le cartelle e corrisponde a quello dichiarato in settings.gradle.kts.
// feature/profile/build.gradle.kts
dependencies {
implementation(project(":core:data"))
implementation(project(":core:designsystem"))
implementation(project(":core:model"))
}implementation e api
La configurazione scelta determina che cosa viene esposto ai consumer:
implementation: la dipendenza è privata. I moduli che dipendono dal suo modulo non possono vederla. È la scelta predefinita.api: la dipendenza viene riesposta (in modo transitivo). La utilizzi solo quando i suoi tipi pubblici provengono da quella dipendenza.
Preferisca quasi sempre implementation: migliora la velocità di compilazione, perché la modifica di una dipendenza nascosta non costringe i consumer a ricompilare.
// core/data/build.gradle.kts
dependencies {
// Repository signatures return :core:model types,
// so consumers need to SEE it -> api
api(project(":core:model"))
// Network is an internal detail -> implementation
implementation(project(":core:network"))
}Perché implementation accelera le compilazioni
Con implementation, Gradle sa che una modifica a una dipendenza nascosta non può influire sull'ABI pubblico del modulo. Di conseguenza, i consumer non devono ricompilare. Con api, una modifica si propaga a ogni consumer transitivo.
Regola pratica: inserisca una dipendenza in api solo se compare nei tipi pubblici del modulo (tipi restituiti, parametri pubblici). In caso contrario, utilizzi implementation.
// Public -> needs api
fun observeUser(): Flow<User> // Flow and User leak out
// Internal -> implementation is enough
private val client: OkHttpClient // never exposedCentralizzare le versioni: il catalogo delle versioni
Con molti moduli, non vorrà ripetere ovunque le versioni delle librerie. Il catalogo delle versioni di Gradle (gradle/libs.versions.toml) definisce una sola volta versioni e alias. Ogni modulo fa riferimento allo stesso alias.
# gradle/libs.versions.toml
[versions]
compose-bom = "2024.09.00"
retrofit = "2.11.0"
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }Utilizzare il catalogo in un modulo
I moduli fanno quindi riferimento alle librerie tramite l'accessor libs generato. L'assenza dei numeri di versione nel file di build fa sì che gli aggiornamenti avvengano in un unico punto.
// core/network/build.gradle.kts
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.retrofit)
}Il peccato capitale: i cicli
Un ciclo si verifica quando il modulo A dipende da B e B dipende nuovamente da A (direttamente o attraverso una catena). Gradle rifiuta di compilare una dipendenza circolare. Questo segnala anche un problema di progettazione: il confine tra i due moduli non è corretto.
// :feature:cart -> implementation(project(":feature:checkout"))
// :feature:checkout -> implementation(project(":feature:cart"))
//
// Gradle error:
// Circular dependency between the following tasks:
// :feature:cart:compile -> :feature:checkout:compile -> :feature:cart:compileSpezzare un ciclo
Per spezzare un ciclo, estragga la parte condivisa in un modulo di livello inferiore da cui entrambi possano dipendere. Se due funzionalità hanno bisogno dei dati l'una dell'altra, quel contratto condiviso appartiene al core, non a una delle due funzionalità.
In questo modo si ripristina il flusso verso il basso: entrambe le funzionalità puntano al core e il core non punta mai nuovamente verso l'alto.
// Before: cart <-> checkout (cycle)
// After: cart -> :core:order <- checkout
// core/order/OrderContract.kt
data class Order(val items: List<CartItem>, val total: Double)
// feature/cart -> implementation(project(":core:order"))
// feature/checkout-> implementation(project(":core:order"))Inversione: dipendere dalle astrazioni
A volte un modulo di basso livello ha bisogno di un comportamento che risiede a un livello superiore. Invece di dipendere verso l'alto, definisca un'interfaccia nel modulo di basso livello e lasci che il modulo di livello superiore fornisca l'implementazione tramite dependency injection. Questa è l'inversione delle dipendenze.
// core/analytics defines the contract
interface AnalyticsLogger {
fun log(event: String)
}
// :app provides the real implementation and injects it down
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
@Provides
fun logger(impl: FirebaseAnalyticsLogger): AnalyticsLogger = impl
}Visualizzare e proteggere il grafo
Può chiedere a Gradle di stampare o generare il grafo dei moduli e persino aggiungere un test che interrompa la compilazione se compare una dipendenza vietata (per esempio, un modulo core che dipende da una funzionalità). Strumenti come il plugin module-graph generano automaticamente un diagramma.
# Print the project structure
./gradlew projects
# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspathUn esempio pulito e aciclico
Ecco un grafo sano. Lo legga dall'alto verso il basso: nessuna freccia punta mai nuovamente verso l'alto e nessuna coppia di moduli punta l'uno all'altro. È esattamente il risultato a cui deve puntare.
// :app
// -> :feature:home -> :core:data -> :core:network -> :core:model
// -> :feature:profile -> :core:data -> :core:database -> :core:model
// -> :core:designsystem
//
// Every path ends at :core:model. No cycles. Builds in parallel.Verifica rapida
Il modulo :core:network utilizza OkHttpClient solo internamente: non compare mai nella firma di una funzione pubblica. Quale configurazione Gradle dovrebbe utilizzare per dichiarare la dipendenza da OkHttp?
Riepilogo: gestire le dipendenze tra moduli
Ha imparato a mantenere sano il grafo dei moduli:
- Dichiari le dipendenze tra moduli con
project(":path"). - Utilizzi
implementationcome impostazione predefinita; ricorra aapisolo per i tipi esposti nella superficie pubblica. - Centralizzi le versioni in un catalogo delle versioni (
libs.versions.toml). - Non crei mai cicli: Gradle li rifiuta; li spezzi estraendo il codice condiviso verso il basso o applicando l'inversione tramite interfacce.
Ora collegherà le funzionalità tra loro tramite la navigazione, senza creare accoppiamento.
Domande Frequenti
La lezione «Gestire le dipendenze tra moduli» è gratuita?
Sì — il testo completo di «Gestire le dipendenze tra moduli» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Android Academy, passa a CoddyKit PRO. Il corso Android Academy include 4 lezioni in totale.
Cosa imparerò in «Gestire le dipendenze tra moduli»?
Mantenga il grafo pulito e aciclico Eserciti Android Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Android Academy?
Non è richiesta alcuna esperienza precedente. Android Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Gestire le dipendenze tra moduli»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Android Academy?
Sì. Ogni lezione Android Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Perché modularizzare
- Moduli feature e core
- Gestire le dipendenze tra moduli
- Navigazione tra moduli