Questa pagina è una guida alla modularizzazione del codice di navigazione. È destinata a integrare le indicazioni generali per la modularizzazione delle app.
Panoramica
La modularizzazione del codice di navigazione è il processo di separazione delle chiavi di navigazione correlate e dei contenuti che rappresentano in singoli moduli. In questo modo, le responsabilità sono chiaramente separate e puoi spostarti tra le diverse funzionalità della tua app.
Per modularizzare il codice di navigazione:
- Crea due sottomoduli:
apieimplper ogni funzionalità della tua app - Inserisci i tasti di navigazione per ogni funzionalità nel relativo modulo
api - Inserisci
entryProviderse i contenuti navigabili per ogni funzionalità nel moduloimplassociato - Fornisci
entryProvidersai moduli principali dell'app, direttamente o utilizzando l'inserimento delle dipendenze
Separa le funzionalità in sottomoduli di API e implementazione
Per ogni funzionalità della tua app, crea due sottomoduli denominati api e impl
(abbreviazione di "implementation"). Utilizza la tabella seguente per decidere dove inserire il codice di navigazione.
Nome modulo |
Contiene |
|
|
|
Contenuti per questa funzionalità, incluse le definizioni di |
Questo approccio consente a una funzionalità di passare a un'altra consentendo ai suoi contenuti,
contenuti nel modulo impl, di dipendere dai tasti di navigazione di un altro
modulo, contenuti nel modulo api di quel modulo.
Voci di navigazione separate utilizzando le funzioni di estensione
In Navigation 3, i contenuti navigabili vengono definiti utilizzando le voci di navigazione. Per separare queste voci in moduli separati, crea funzioni di estensione su EntryProviderScope e spostale nel modulo impl per la funzionalità in questione.
Questi sono noti come generatori di voci.
Il seguente esempio di codice mostra un generatore di voci che crea due voci di navigazione.
// import androidx.navigation3.runtime.EntryProviderScope // import androidx.navigation3.runtime.NavKey fun EntryProviderScope<NavKey>.featureAEntryBuilder() { entry<KeyA> { ContentRed("Screen A") { // Content for screen A } } entry<KeyA2> { ContentGreen("Screen A2") { // Content for screen A2 } } }
Chiama questa funzione utilizzando il DSL entryProvider quando definisci il tuo entryProvider nel modulo principale dell'app.
// import androidx.navigation3.runtime.entryProvider // import androidx.navigation3.ui.NavDisplay NavDisplay( entryProvider = entryProvider { featureAEntryBuilder() }, // ... )
Utilizza l'inserimento delle dipendenze per aggiungere voci all'app principale
Nell'esempio di codice precedente, ogni generatore di voci viene chiamato direttamente dall'app principale utilizzando il DSL entryProvider. Se la tua app ha molte schermate o moduli delle funzionalità, questo pattern potrebbe non essere scalabile.
Per risolvere il problema, fai in modo che ogni modulo della funzionalità contribuisca con i propri generatori di voci all'attività dell'app utilizzando l'inserimento delle dipendenze.
Ad esempio, il seguente codice utilizza i multibinding Dagger,
in particolare @IntoSet, per inserire i builder di voci in un Set di proprietà di
MainActivity. Questi vengono poi chiamati in modo iterativo all'interno di entryProvider,
eliminando la necessità di chiamare esplicitamente numerose funzioni di creazione delle voci.
Modulo della funzionalità
// import dagger.Module // import dagger.Provides // import dagger.hilt.InstallIn // import dagger.hilt.android.components.ActivityRetainedComponent // import dagger.multibindings.IntoSet @Module @InstallIn(ActivityRetainedComponent::class) object FeatureAModule { @IntoSet @Provides fun provideFeatureAEntryBuilder() : EntryProviderScope<NavKey>.() -> Unit = { featureAEntryBuilder() } }
Modulo dell'app
// import android.os.Bundle // import androidx.activity.ComponentActivity // import androidx.activity.compose.setContent // import androidx.navigation3.runtime.EntryProviderScope // import androidx.navigation3.runtime.NavKey // import androidx.navigation3.runtime.entryProvider // import androidx.navigation3.ui.NavDisplay // import javax.inject.Inject class MainActivity : ComponentActivity() { @Inject lateinit var entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<NavKey>.() -> Unit> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { NavDisplay( entryProvider = entryProvider { entryBuilders.forEach { builder -> this.builder() } }, // ... ) } } }
Se le voci di navigazione devono navigare, ad esempio contengono elementi UI che navigano verso nuove schermate, inserisci un oggetto in grado di modificare lo stato di navigazione dell'app in ogni funzione del builder.
Modularizza i matcher dei deep link
Se la tua app supporta i deep link ed è modularizzata, ogni modulo della funzionalità deve definire le istanze DeepLinkMatcher per le destinazioni di sua proprietà.
Per raggruppare tutti i matcher della tua app, puoi utilizzare i multibinding di inserimento delle dipendenze. Ad esempio, ogni modulo della funzionalità può
contribuire con i propri matcher a un multibinding Dagger @IntoSet:
Modulo della funzionalità
@Module @InstallIn(ActivityRetainedComponent::class) object FeatureADeepLinkModule { @IntoSet @Provides fun provideUserMatcher(): DeepLinkMatcher<*, *> { return UriDeepLinkMatcher( DeepLinkUri("www.example.com/users/{id}"), serializer<UserProfileKey>() ) } }
Poi, nel modulo principale dell'app, ad esempio in MainActivity, puoi inserire l'insieme di matcher e utilizzarli per trovare le richieste in arrivo corrispondenti:
Modulo dell'app
@AndroidEntryPoint class MainActivityWithDI : ComponentActivity() { @Inject lateinit var deepLinkMatchers: Set<@JvmSuppressWildcards DeepLinkMatcher<*, *>> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val request = DeepLinkRequest(intent = intent) val matchResult = deepLinkMatchers .mapNotNull { it.match(request) } .maxOrNull() val backStack = when (matchResult) { null -> listOf(HomeKey) is BackStackMatchResult<*, *> -> { @Suppress("UNCHECKED_CAST") matchResult.backStack as List<NavKey> } else -> listOf(matchResult.key) } } }
Per saperne di più sulla definizione e sulla gestione dei deep link, consulta Supportare i deep link.
Risorse
Per esempi di codice che mostrano come modularizzare il codice di Navigation 3, consulta:
- The Navigation 3 architecture code recipes
- Il percorso di apprendimento sulla modularizzazione dall'app Now in Android
- Androidify