Ten przewodnik opisuje proces zastępowania tras opartych na ciągach znaków serializowalnymi typami Kotlin, aby zapewnić bezpieczeństwo w czasie kompilacji i wyeliminować awarie w czasie działania spowodowane literówkami lub nieprawidłowymi typami argumentów.
Wymagania wstępne
Przed rozpoczęciem migracji sprawdź, czy Twój projekt spełnia te wymagania:
- Wersja nawigacji: zaktualizuj nawigację Jetpack do wersji 2.8.0 lub nowszej.
- Wtyczka serializacji Kotlin:
- Dodaj wtyczkę do
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" }
- Dodaj zależności do plików
build.gradle.ktsnajwyższego poziomu ibuild.gradle.ktsna poziomie modułu.
Krok 1. Określ miejsca docelowe
Zastąp stałe ciągi znaków trasy obiektami i klasami @Serializable.
- W przypadku ekranów bez argumentów: użyj
data object - W przypadku ekranów z argumentami: użyj
data class
Przed (na podstawie ciągu znaków):
const val ROUTE_HOME = "home"
const val ROUTE_PROFILE = "profile/{userId}"
Po (bezpieczne typy):
import kotlinx.serialization.Serializable
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
Krok 2. Zaktualizuj konfigurację NavHost
Zaktualizuj NavHost, aby używać nowych typów ogólnych w funkcji composable i dialog.
Przed:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(...) }
composable("profile/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId)
}
}
Po:
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(...)
}
composable<Profile> { backStackEntry ->
// The library automatically handles argument extraction
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(profile.userId)
}
}
Krok 3. Wdróż wywołania nawigacji bezpieczne pod względem typów
Zastąp wywołania nawigacji z interpolacją ciągów instancjami klas.
Przed:
navController.navigate("profile/user123")
Po:
navController.navigate(Profile(userId = "user123"))
Krok 4. Dostęp do argumentów w klasach ViewModel
Jeśli używasz ViewModel, możesz teraz wyodrębnić obiekt trasy bezpośrednio z SavedStateHandle.
Implementacja:
class ProfileViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
// Automatically parses arguments into the Profile class
private val profile = savedStateHandle.toRoute<Profile>()
val userId = profile.userId
}
Krok 5. (Zaawansowane) Obsługa typów niestandardowych
Jeśli musisz przekazywać złożone klasy danych (nie tylko typy proste), musisz zdefiniować niestandardowy NavType.
- Utwórz typ niestandardowy:
```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)
) { ... }
Sprawdzone metody i wskazówki
- Zamknięte hierarchie: w przypadku dużych aplikacji grupuj trasy za pomocą zamkniętego interfejsu lub klasy, aby zachować porządek w strukturze nawigacji.
- Instancje obiektów: w przypadku tras bez parametrów zawsze używaj
objectzamiastclass, aby uniknąć niepotrzebnych przydziałów. - Typy dopuszczające wartość null: nowy interfejs API obsługuje typy dopuszczające wartość null (np.
data class Search(val query: String?)) i automatycznie podaje wartości domyślne. - Testowanie: użyj
navController.currentBackStackEntry?.hasRoute<T>(), aby podczas testów interfejsu sprawdzić bieżące miejsce docelowe w sposób bezpieczny pod względem typów.