Navigationscode modularisieren

Auf dieser Seite finden Sie eine Anleitung zur Modularisierung Ihres Navigationscodes. Sie soll die allgemeine Anleitung zur App-Modularisierung ergänzen.

Übersicht

Bei der Modularisierung Ihres Navigationscodes werden verwandte Navigationsschlüssel und die Inhalte, die sie darstellen, in einzelne Module aufgeteilt. Dadurch wird eine klare Trennung der Verantwortlichkeiten erreicht und Sie können zwischen verschiedenen Funktionen in Ihrer App navigieren.

So modularisieren Sie Ihren Navigationscode:

  • Erstellen Sie für jede Funktion in Ihrer App zwei Untermodule: api und impl.
  • Platzieren Sie die Navigationsschlüssel für jede Funktion im Modul api.
  • Platzieren Sie entryProviders und navigierbare Inhalte für jede Funktion im zugehörigen Modul impl.
  • Stellen Sie entryProviders für Ihre Haupt-App-Module bereit, entweder direkt oder über die Abhängigkeitsinjektion.

Funktionen in Untermodule für API und Implementierung aufteilen

Erstellen Sie für jede Funktion in Ihrer App zwei Untermodule mit den Namen api und impl (kurz für „Implementierung“). Anhand der folgenden Tabelle können Sie entscheiden, wo Sie den Navigationscode platzieren.

Modulname

Enthält

api

Navigationsschlüssel

impl

Inhalte für diese Funktion, einschließlich Definitionen für NavEntrys und den entryProvider. Weitere Informationen finden Sie unter Schlüssel in Inhalte auflösen.

Mit diesem Ansatz kann eine Funktion zu einer anderen navigieren, indem ihre Inhalte, die im Modul impl enthalten sind, von den Navigationsschlüsseln eines anderen Moduls abhängen, die im Modul api dieses Moduls enthalten sind.

Diagramm mit Abhängigkeiten von Funktionsmodulen, das zeigt, wie `impl`-Module von `api`-Modulen abhängen können.
Abbildung 1. Abhängigkeitsdiagramm für Funktionsmodule, das zeigt, wie Implementierungsmodule von API-Modulen abhängen können.

Navigationseinträge mit Erweiterungsfunktionen trennen

In Navigation 3 werden navigierbare Inhalte mit Navigationseinträgen definiert. Wenn Sie diese Einträge in separate Module aufteilen möchten, erstellen Sie Erweiterungsfunktionen für EntryProviderScope und verschieben Sie sie in das impl Modul für diese Funktion. Diese werden als Eintrags-Builder bezeichnet.

Das folgende Codebeispiel zeigt einen Eintrags-Builder, der zwei Navigationseinträge erstellt.

// 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
        }
    }
}

Rufen Sie diese Funktion mit der entryProvider DSL auf, wenn Sie entryProvider in Ihrem Haupt-App-Modul definieren.

// import androidx.navigation3.runtime.entryProvider
// import androidx.navigation3.ui.NavDisplay
NavDisplay(
    entryProvider = entryProvider {
        featureAEntryBuilder()
    },
    // ...
)

Einträge mit Abhängigkeitsinjektion zur Haupt-App hinzufügen

Im vorherigen Codebeispiel wird jeder Eintrags-Builder direkt von der Haupt-App mit der entryProvider-DSL aufgerufen. Wenn Ihre App viele Bildschirme oder Funktionsmodule hat, lässt sich dieses Muster möglicherweise nicht gut skalieren.

Um dieses Problem zu beheben, können Sie mit der Abhängigkeitsinjektion dafür sorgen, dass jedes Funktionsmodul seine Eintrags-Builder zur Aktivität der App beiträgt.

Im folgenden Code werden beispielsweise Dagger-Multibindungen, insbesondere @IntoSet, verwendet, um die Eintrags-Builder in ein Set einzufügen, das zu MainActivity gehört. Diese werden dann iterativ in entryProvider aufgerufen, sodass es nicht erforderlich ist, zahlreiche Eintrags-Builder-Funktionen explizit aufzurufen.

Funktionsmodul

// 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()
    }
}

App-Modul

// 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() }
                },
                // ...
            )
        }
    }
}

Wenn Ihre Navigationseinträge navigieren müssen, z. B. UI-Elemente enthalten, die zu neuen Bildschirmen führen, fügen Sie jeder Builder-Funktion ein Objekt hinzu, mit dem der Navigationsstatus der App geändert werden kann.

Wenn Ihre App Deeplinks unterstützt und modularisiert ist, sollte jedes Funktionsmodul die DeepLinkMatcher Instanzen für die zugehörigen Ziele definieren.

Um alle Matcher aus Ihrer App zusammenzufassen, können Sie Multibindungen für die Abhängigkeitsinjektion verwenden. Jedes Funktionsmodul kann beispielsweise seine Matcher zu einer Dagger-Multibindung @IntoSet beitragen:

Funktionsmodul

In Ihrem Haupt-App-Modul, z. B. in MainActivity, können Sie dann die Menge der Matcher einfügen und damit eingehende Anfragen abgleichen:

App-Modul

Weitere Informationen zum Definieren und Verarbeiten von Deeplinks finden Sie unter Deeplinks unterstützen.

Ressourcen

Codebeispiele zur Modularisierung von Navigation 3-Code finden Sie hier: