Урок по треку Android 7 мин

Навигация за 7 минут

Navigation Compose, типобезопасные маршруты, back stack, аргументы и deep links.

Навигация в Android прошла путь от startActivity и транзакций фрагментов до одной Activity с Navigation Component и Navigation Compose. На собесе спрашивают back stack, передачу аргументов, типобезопасность, deep links и навигацию в многомодульном проекте.

Single Activity

Современный подход: одна Activity, экраны это composable-функции (или фрагменты), переходы делает NavController. Плюсы: общий back stack и анимации, проще передавать данные, ViewModel можно привязать к графу. Несколько Activity остаются для отдельных точек входа (например, экран звонка) и легаси.

Navigation Compose с типобезопасными маршрутами

С Navigation 2.8 маршруты описывают сериализуемыми классами вместо строк:

@Serializable object Home
@Serializable data class Article(val id: String)

@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
    NavHost(navController, startDestination = Home) {
        composable<Home> {
            HomeScreen(onOpen = { id -> navController.navigate(Article(id)) })
        }
        composable<Article> { entry ->
            val route = entry.toRoute<Article>()
            ArticleScreen(route.id)
        }
    }
}

Во ViewModel аргументы доступны через SavedStateHandle: savedStateHandle.toRoute<Article>(). Опечатка в имени аргумента теперь ошибка компиляции, а не падение.

Правило: передавать идентификаторы, а не объекты. Аргументы хранятся в Bundle back stack, большой объект замедлит навигацию и может не поместиться. Экран по id сам загрузит данные из репозитория.

В 2025 году Google представил Navigation 3 (androidx.navigation3): back stack это обычный список, которым владеет приложение, а NavDisplay его отображает. Основной объём кода на собесах пока про Navigation 2.

Back stack

NavController хранит стек NavBackStackEntry. Каждая запись это LifecycleOwner, ViewModelStoreOwner и SavedStateRegistryOwner: у экрана своя ViewModel, которая живёт, пока запись в стеке.

navController.navigate(Home) {
    popUpTo<Login> { inclusive = true }   // убрать вход из стека
    launchSingleTop = true                // не дублировать, если уже наверху
}
navController.popBackStack()

Нижняя навигация: при переключении вкладок используют popUpTo(graph.findStartDestination().id) { saveState = true }, launchSingleTop = true и restoreState = true, чтобы каждая вкладка сохраняла свой стек.

Вернуть результат на предыдущий экран: через previousBackStackEntry?.savedStateHandle?.set("key", value) или через общую ViewModel графа. Для однократных результатов этого хватает.

Вложенные графы

navigation<AuthGraph>(startDestination = Login) { composable<Login> {}; composable<Register> {} } группирует экраны в поток. ViewModel можно привязать к графу (navController.getBackStackEntry<AuthGraph>() как владелец), и все экраны потока получат одну.

Deep links

composable<Article>(
    deepLinks = listOf(navDeepLink<Article>(basePath = "https://example.com/article"))
) { }

В манифесте Activity нужен intent-filter с VIEW, BROWSABLE и схемой. Для https-ссылок, которые открываются без диалога выбора, нужны App Links: android:autoVerify="true" и файл /.well-known/assetlinks.json на домене. При открытии по deep link Navigation строит синтетический back stack, чтобы «Назад» вело на родительский экран, а не закрывало приложение.

Системная кнопка «Назад»

С Android 13 появился predictive back: система показывает анимацию предпросмотра, а приложение должно сообщать заранее, обработает ли оно жест. Поэтому onBackPressed() устарел, используют OnBackPressedDispatcher с OnBackPressedCallback, в Compose BackHandler { } и PredictiveBackHandler. Navigation обрабатывает «Назад» сам.

Многомодульная навигация

Feature-модули не должны зависеть друг от друга. Обычный подход: каждая фича экспортирует функцию-расширение NavGraphBuilder.articleScreen(onBack: () -> Unit) и маршрут, а связывает графы модуль app. Переходы между фичами передаются колбэками или через API-модули с маршрутами.

Частые ошибки

  • Передача больших объектов в аргументах навигации.
  • Вызов navigate прямо в теле composable (вызывается при каждой рекомпозиции): только в колбэках или эффектах.
  • NavController во ViewModel.
  • Двойной клик открывает экран дважды: нет launchSingleTop или защиты от повторного нажатия.
  • Не продуманный стек после логина: «Назад» возвращает на экран входа.

Что спросят на собесе

  • Как устроен back stack в Navigation и что такое NavBackStackEntry?
  • Как передать аргументы и почему лучше передавать id?
  • Как вернуть результат на предыдущий экран?
  • Как работают deep links и App Links?
  • Как организовать навигацию в многомодульном проекте?
  • Как работает нижняя навигация с сохранением стека вкладок?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.