В этом руководстве описывается процесс замены строковых маршрутов сериализуемыми типами Kotlin для обеспечения безопасности на этапе компиляции и устранения сбоев во время выполнения, вызванных опечатками или некорректными типами аргументов.
Предварительные требования
Перед началом миграции убедитесь, что ваш проект соответствует следующим требованиям:
- Версия навигации : Обновите Jetpack Navigation до версии 2.8.0 или выше.
- Плагин сериализации Kotlin :
- Добавьте плагин в файл
libs.versions.toml:
[libraries]
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
- Добавьте зависимости в главный файл
build.gradle.ktsи в файлbuild.gradle.ktsна уровне модуля.
Шаг 1: Определите пункты назначения
Замените ваши постоянные строковые маршруты на объекты и классы с @Serializable .
- Для экранов без аргументов : используйте
data object - Для экранов с аргументами : используйте
data class
До (на основе строк):
const val ROUTE_HOME = "home"
const val ROUTE_PROFILE = "profile/{userId}"
После (безопасного ввода):
import kotlinx.serialization.Serializable
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
Шаг 2: Обновите конфигурацию NavHost.
Обновите NavHost , чтобы использовать новые универсальные типы в функциях composable и dialog .
До:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(...) }
composable("profile/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId)
}
}
После:
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(...)
}
composable<Profile> { backStackEntry ->
// The library automatically handles argument extraction
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(profile.userId)
}
}
Шаг 3: Реализация типобезопасных вызовов навигации
Замените вызовы навигации с интерполяцией строк экземплярами классов.
До:
navController.navigate("profile/user123")
После:
navController.navigate(Profile(userId = "user123"))
Шаг 4: Доступ к аргументам в ViewModel.
Если вы используете ViewModel , теперь вы можете извлечь объект маршрута непосредственно из SavedStateHandle .
Выполнение:
class ProfileViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
// Automatically parses arguments into the Profile class
private val profile = savedStateHandle.toRoute<Profile>()
val userId = profile.userId
}
Шаг 5: (Расширенные) Обработка пользовательских типов
Если вам необходимо передавать сложные классы данных (а не только примитивы), необходимо определить пользовательский NavType ).
- Создайте пользовательский тип : ```kotlin val SearchFilterType = object : NavType`
(isNullableAllowed = false) { override fun get(bundle: Bundle, key: String): SearchFilter? = Json.decodeFromString(bundle.getString(key) ?: return null)
override fun parseValue(value: String): SearchFilter =
Json.decodeFromString(Uri.decode(value))
override fun put(bundle: Bundle, key: String, value: SearchFilter) {
bundle.putString(key, Json.encodeToString(value))
}
}
2. **Register it in the Graph**:
```kotlin
composable<Search>(
typeMap = mapOf(typeOf<SearchFilter>() to SearchFilterType)
) { ... }
Передовые методы и советы
- Иерархии с закрытым интерфейсом : Для больших приложений группируйте маршруты, используя закрытый интерфейс или класс, чтобы сохранить упорядоченную структуру навигации.
- Экземпляры объектов : Для маршрутов без параметров всегда используйте
objectвместоclass, чтобы избежать ненужного выделения памяти. - Типы, допускающие значение null : Новый API поддерживает типы, допускающие значение null (например,
data class Search(val query: String?)) и автоматически предоставляет значения по умолчанию. - Тестирование : Используйте
navController.currentBackStackEntry?.hasRoute<T>()для проверки текущего пункта назначения типобезопасным способом во время тестирования пользовательского интерфейса.