Modularizzare il codice di navigazione

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: api e impl per ogni funzionalità della tua app
  • Inserisci i tasti di navigazione per ogni funzionalità nel relativo modulo api
  • Inserisci entryProviders e i contenuti navigabili per ogni funzionalità nel modulo impl associato
  • Fornisci entryProviders ai 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

api

tasti di navigazione

impl

Contenuti per questa funzionalità, incluse le definizioni di NavEntry e entryProvider. Consulta anche la sezione Risolvere le chiavi per i contenuti.

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.

Diagramma delle dipendenze dei moduli delle funzionalità che mostra come i moduli "impl" possono dipendere dai moduli "api".
Figura 1. Diagramma delle dipendenze dei moduli delle funzionalità che mostra come i moduli di implementazione possono dipendere dai moduli API.

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.

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à

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

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: