Nawigacja między modułami
Łącz funkcje bez ścisłego powiązania
Nawigacja między modułami to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.
Wyzwanie związane z nawigacją
Gdy funkcjonalności znajdują się w osobnych modułach, pojawia się nowe pytanie: jak :feature:home może otworzyć ekran w :feature:profile bez zależności od tego modułu? Jeśli funkcjonalności będą importować się bezpośrednio, powstanie silne powiązanie i ryzyko cykli.
W tej lekcji nauczysz się łączyć funkcjonalności za pomocą nawigacji, zachowując ich niezależność.
Gdzie znajduje się NavHost
Pojedynczy NavHost znajduje się w module :app — jedynym miejscu, które może znać wszystkie funkcjonalności. Każda funkcjonalność dostarcza swoje destynacje, a :app składa je w jeden graf.
// app/AppNavHost.kt
@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
NavHost(navController, startDestination = HomeRoute) {
homeScreen(onProfileClick = { navController.navigate(ProfileRoute) })
profileScreen(onBack = { navController.popBackStack() })
}
}Trasy bezpieczne typowo
Nowoczesny Navigation Compose obsługuje trasy bezpieczne typowo: trasa jest obiektem @Serializable lub klasą danych, a nie magicznym ciągiem znaków. Każda funkcjonalność definiuje własny typ trasy w swoim module, dzięki czemu sama zarządza swoim kontraktem nawigacji.
// feature/profile/ProfileRoute.kt
import kotlinx.serialization.Serializable
@Serializable
data class ProfileRoute(val userId: String)Funkcjonalności udostępniają rozszerzenia NavGraphBuilder
Najważniejszy mechanizm polega na tym, że każda funkcjonalność udostępnia funkcję rozszerzającą NavGraphBuilder, która rejestruje jej destynację. Funkcjonalność zarządza swoim ekranem, a :app tylko wywołuje tę funkcję. Funkcjonalność nigdy nie odwołuje się do innych funkcjonalności.
// feature/profile/ProfileNavigation.kt
fun NavGraphBuilder.profileScreen(onBack: () -> Unit) {
composable<ProfileRoute> { backStackEntry ->
val route: ProfileRoute = backStackEntry.toRoute()
ProfileScreen(userId = route.userId, onBack = onBack)
}
}Rozdzielanie zależności za pomocą callbacków nawigacji
Funkcjonalność nigdy nie może bezpośrednio wywoływać navigate(SomeOtherFeatureRoute), ponieważ wymagałoby to zależności od innej funkcjonalności. Zamiast tego udostępnia callbacki lambda, takie jak onProfileClick. To moduł :app decyduje, dokąd faktycznie prowadzą.
// feature/home/HomeNavigation.kt
fun NavGraphBuilder.homeScreen(onProfileClick: (String) -> Unit) {
composable<HomeRoute> {
HomeScreen(onUserClick = { userId -> onProfileClick(userId) })
}
}
// :home does NOT know ProfileRoute exists.Moduł :app łączy wszystko
Tylko moduł :app zna wszystkie trasy i łączy callbacki z rzeczywistą nawigacją. To jedyne miejsce, w którym powiązania są akceptowalne — jego zadaniem jest właśnie składanie całości.
// app/AppNavHost.kt
NavHost(navController, startDestination = HomeRoute) {
homeScreen(
onProfileClick = { userId ->
navController.navigate(ProfileRoute(userId)) // app knows both
}
)
profileScreen(onBack = { navController.popBackStack() })
}Bezpieczne przekazywanie argumentów
Ponieważ trasy są klasami danych @Serializable, argumenty są sprawdzane pod kątem typów podczas kompilacji. Odczytasz je ponownie za pomocą toRoute() wewnątrz destynacji. Nie musisz już parsować ciągów znaków ani obawiać się awarii w czasie działania związanych z arguments?.getString(...).
// Reading arguments inside the destination
composable<ProfileRoute> { entry ->
val args: ProfileRoute = entry.toRoute()
ProfileScreen(userId = args.userId)
}
// Or directly in a ViewModel via SavedStateHandle
val route: ProfileRoute = savedStateHandle.toRoute()Udostępnianie typów tras przez moduł api
Czasami jedna funkcjonalność rzeczywiście musi nawigować do innej i potrzebuje typu jej trasy. Zamiast zależeć od całej funkcjonalności, udostępnij samą trasę w niewielkim module :feature:profile:api, zawierającym wyłącznie trasę @Serializable. Ciężki moduł :impl pozostaje prywatny.
// feature/profile/api -> only the route type
@Serializable
data class ProfileRoute(val userId: String)
// feature/home/build.gradle.kts
// home may depend on the lightweight api to build the route,
// but never on :feature:profile:impl
implementation(project(":feature:profile:api"))Zagnieżdżona nawigacja w funkcjonalności
Funkcjonalność z kilkoma ekranami może udostępniać cały zagnieżdżony graf za pomocą navigation<T>. Funkcjonalność zarządza swoim wewnętrznym przepływem, a :app tylko podłącza graf w jednym punkcie wejścia.
// feature/onboarding/OnboardingNavigation.kt
fun NavGraphBuilder.onboardingGraph(onFinished: () -> Unit) {
navigation<OnboardingGraph>(startDestination = WelcomeRoute) {
composable<WelcomeRoute> { WelcomeScreen() }
composable<PermissionsRoute> { PermissionsScreen(onDone = onFinished) }
}
}Deep linki między modułami
Trasy bezpieczne typowo obsługują również deep linki. Funkcjonalność deklaruje wzorzec URI deep linku dla swojej destynacji, a NavHost modułu :app kieruje przychodzący link do właściwego ekranu funkcjonalności — bez konieczności importowania innej funkcjonalności.
composable<ProfileRoute>(
deepLinks = listOf(
navDeepLink<ProfileRoute>(basePath = "https://myapp.com/profile")
)
) { entry ->
ProfileScreen(userId = entry.toRoute<ProfileRoute>().userId)
}Podsumowanie wzorca rozdzielania zależności
Zasady w skrócie są proste:
- Każda funkcjonalność zarządza własnym typem trasy i rozszerzeniem NavGraphBuilder.
- Funkcjonalności komunikują zamiary za pomocą callbacków lambda, a nie bezpośredniej nawigacji.
- Tylko :app zna wszystkie funkcjonalności i łączy callbacki z rzeczywistymi trasami.
- Jeśli trasę trzeba współdzielić, udostępnij ją za pomocą niewielkiego modułu :api.
Dzięki temu funkcjonalności pozostają niezależne, mogą korzystać z pamięci podręcznej kompilacji i nie tworzą cykli.
Szybkie sprawdzenie
W aplikacji wielomodułowej :feature:home musi przenieść użytkownika do ekranu w :feature:profile. Jaki jest najczystszy sposób na zachowanie niezależności tych funkcjonalności?
Podsumowanie: nawigacja między modułami
Nauczyłeś się łączyć funkcjonalności bez ich wzajemnego powiązania:
- Pojedynczy
NavHostznajduje się w cienkim module :app. - Każda funkcjonalność zarządza trasą
@Serializablei rozszerzeniemNavGraphBuilder. - Funkcjonalności udostępniają callbacki zamiast bezpośrednio nawigować do innych funkcjonalności.
- Trasę należy współdzielić wyłącznie za pośrednictwem niewielkiego modułu :api, gdy jest to naprawdę potrzebne; deep linki i zagnieżdżone grafy działają według tego samego wzorca.
To kończy temat architektury aplikacji wielomodułowej: możesz teraz dzielić, łączyć i nawigować po skalowalnej bazie kodu Android.
Ucz się Kotlin dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 36
- Lekcje
- 152
Często zadawane pytania
Czy lekcja „Nawigacja między modułami” jest bezpłatna?
Tak — pełny tekst „Nawigacja między modułami” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Nawigacja między modułami”?
Łącz funkcje bez ścisłego powiązania Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Android Academy?
Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Nawigacja między modułami”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?
Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Dlaczego warto stosować moduły
- Moduły funkcji i moduły podstawowe
- Zarządzanie zależnościami modułów
- Nawigacja między modułami