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:
apiiimpldla każdej funkcji w aplikacji. - Umieść klucze nawigacji dla każdej funkcji w jej module
api. - Umieść
entryProvidersi treści, po których można nawigować, dla każdej funkcji w powiązanym moduleimpl. - Udostępnij
entryProvidersgłó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: |
|
|
|
treści dla tej funkcji, w tym definicje
|
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.
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.
Modularyzacja dopasowywania precyzyjnych linków
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
@Module @InstallIn(ActivityRetainedComponent::class) object FeatureADeepLinkModule { @IntoSet @Provides fun provideUserMatcher(): DeepLinkMatcher<*, *> { return UriDeepLinkMatcher( DeepLinkUri("www.example.com/users/{id}"), serializer<UserProfileKey>() ) } }
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
@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) } } }
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: