Podziel kod nawigacji na moduły

Ta strona zawiera przewodnik po modularyzacji kodu nawigacji. Ma ona uzupełniać ogólne wskazówki dotyczące modularyzacji aplikacji.

Przegląd

Modularyzacja kodu nawigacji to proces dzielenia powiązanych kluczy nawigacji i reprezentowanych przez nie treści na osobne moduły. Pozwala to wyraźnie oddzielić zakresy odpowiedzialności i umożliwia nawigowanie między różnymi funkcjami aplikacji.

Aby modularyzować kod nawigacji:

  • Utwórz 2 podmoduły: api i impl dla każdej funkcji w aplikacji.
  • Umieść klucze nawigacji dla każdej funkcji w jej module api.
  • Umieść entryProviders i treści, po których można nawigować, dla każdej funkcji w powiązanym module impl.
  • Udostępnij entryProviders głównym modułom aplikacji bezpośrednio lub za pomocą wstrzykiwania zależności.

Dzielenie funkcji na podmoduły api i implementacji

Dla każdej funkcji w aplikacji utwórz 2 podmoduły o nazwach api i impl (skrót od „implementation”, czyli „implementacja”). W tej tabeli znajdziesz informacje o tym, gdzie umieścić kod nawigacji.

Nazwa modułu

Zawiera:

api

klucze nawigacji,

impl

treści dla tej funkcji, w tym definicje NavEntry i entryProvider. Zobacz też Rozwiązywanie kluczy do treści.

To podejście umożliwia jednej funkcji nawigowanie do innej, ponieważ jej treści zawarte w module impl mogą zależeć od kluczy nawigacji innego modułu, które znajdują się w module api tego modułu.

Diagram zależności modułu 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 api.

Dzielenie wpisów nawigacji za pomocą funkcji rozszerzających

W Navigation 3 treści, po których można nawigować, są definiowane za pomocą wpisów nawigacji. Aby podzielić te wpisy na osobne moduły, utwórz funkcje rozszerzające w EntryProviderScope i przenieś je do modułu impl dla danej funkcji. Są one nazywane konstruktorami wpisów.

Ten przykład kodu pokazuje konstruktora wpisów, który tworzy 2 wpisy nawigacji.

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

Dodawanie wpisów do głównej aplikacji za pomocą wstrzykiwania zależności

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

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

Na przykład ten kod używa multibindings Dagger, a konkretnie @IntoSet, aby wstrzyknąć konstruktory wpisów do Set należącego do MainActivity. Są one następnie wywoływane iteracyjnie w entryProvider, co eliminuje konieczność jawnego wywoływania wielu funkcji konstruktora 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 nawigacji muszą nawigować (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.

Jeśli Twoja aplikacja obsługuje precyzyjne linki i jest modularyzowana, każdy moduł funkcji powinien definiować instancje DeepLinkMatcher dla należących do niego miejsc docelowych.

Aby zebrać wszystkie dopasowania z całej aplikacji, możesz użyć multibindings wstrzykiwania zależności. Na przykład każdy moduł funkcji może dodawać swoje dopasowania do multibinding Dagger @IntoSet:

Moduł funkcji

Następnie w głównym module aplikacji, np. w MainActivity, możesz wstrzyknąć zbiór dopasowań i użyć ich do dopasowania przychodzących żądań:

Moduł aplikacji

Więcej informacji o definiowaniu i obsłudze precyzyjnych linków znajdziesz w artykule Obsługa precyzyjnych linków.

Zasoby

Przykłady kodu pokazujące, jak modularyzować kod Navigation 3: