Podziel kod nawigacji na moduły

Na tej stronie znajdziesz wskazówki dotyczące modularyzacji kodu nawigacji. Ma ona uzupełniać ogólne wskazówki dotyczące modularizacji aplikacji.

Omówienie

Modularyzacja kodu nawigacji to proces dzielenia powiązanych kluczy nawigacji i reprezentowanych przez nie treści na osobne moduły. Zapewnia to wyraźne rozdzielenie zakresów odpowiedzialności i umożliwia poruszanie się między różnymi funkcjami aplikacji.

Aby podzielić kod nawigacji na moduły:

  • Utwórz 2 podmoduły: apiimpl dla każdej funkcji w aplikacji.
  • Umieść klucze nawigacyjne dla każdej funkcji w jej module api.
  • Umieść entryProviders i treści, po których można nawigować, dla każdej funkcji w odpowiednim module impl.
  • Przekaż entryProviders do głównych modułów aplikacji bezpośrednio lub za pomocą wstrzykiwania zależności.

Podzielenie funkcji na podmoduły API i implementacji

Dla każdej funkcji w aplikacji utwórz 2 podmoduły o nazwach apiimpl (skrót od „implementation”). Z tabeli poniżej dowiesz się, gdzie umieścić kod nawigacji.

Nazwa modułu

Zawiera

api

klawisze nawigacyjne,

impl

Treści tej funkcji, w tym definicje NavEntryentryProvider. Zobacz też rozwiązywanie kluczy do treści.

Dzięki temu jedna funkcja może przechodzić do innej, ponieważ jej treść, zawarta w module impl, zależy od klawiszy nawigacyjnych innego modułu, zawartych w module api tego modułu.

Diagram zależności modułów funkcji pokazujący, jak moduły `impl` mogą zależeć od modułów `api`.
Rysunek 1. Diagram zależności modułów funkcji pokazujący, jak moduły implementacji mogą zależeć od modułów interfejsu API.

Oddzielanie wpisów nawigacyjnych za pomocą funkcji rozszerzeń

W Navigation 3 treści, po których można się poruszać, są definiowane za pomocą wpisów nawigacyjnych. Aby rozdzielić te wpisy na osobne moduły, utwórz funkcje rozszerzające w EntryProviderScope i przenieś je do modułu impl dla tej funkcji. Są one nazywane kreatorami wpisów.

Poniższy przykład kodu pokazuje konstruktora wpisów, który tworzy 2 wpisy nawigacyjne.

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

Wywołaj tę funkcję za pomocą entryProvider DSL podczas definiowania entryProvider w głównym module aplikacji.

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

Używanie wstrzykiwania zależności do dodawania wpisów do głównej aplikacji

W poprzednim przykładzie kodu każdy konstruktor wpisów jest wywoływany bezpośrednio przez główną aplikację za pomocą języka DSL entryProvider. Jeśli aplikacja ma wiele ekranów lub modułów funkcji, może to nie być skalowalne.

Aby to rozwiązać, spraw, aby każdy moduł funkcji przekazywał swoje konstruktory wpisów do aktywności aplikacji za pomocą wstrzykiwania zależności.

Na przykład ten kod używa wielokrotnych powiązań Daggera, a konkretnie @IntoSet, do wstrzykiwania konstruktorów wpisów do Set należącego do MainActivity. Są one następnie wywoływane iteracyjnie w funkcji entryProvider, co eliminuje konieczność jawnego wywoływania wielu funkcji tworzenia wpisów.

Moduł funkcji

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

Moduł aplikacji

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

Jeśli wpisy nawigacyjne wymagają nawigacji (np. zawierają elementy interfejsu, które prowadzą do nowych ekranów), wstrzyknij do każdej funkcji konstruktora obiekt, który może modyfikować stan nawigacji aplikacji.

Materiały

Przykłady kodu pokazujące, jak podzielić kod Navigation 3 na moduły, znajdziesz w tych artykułach: