Questa guida descrive il processo di sostituzione delle route basate su stringhe con tipi Kotlin serializzabili per ottenere la sicurezza in fase di compilazione ed eliminare gli arresti anomali del runtime causati da errori di battitura o tipi di argomenti errati.
Prerequisiti
Prima di iniziare la migrazione, verifica che il progetto soddisfi i seguenti requisiti:
- Versione di Navigation: esegui l'aggiornamento a Jetpack Navigation 2.8.0 o versioni successive
- Plug-in di serializzazione Kotlin:
- Aggiungi il plug-in a
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" }
- Aggiungi le dipendenze a
build.gradle.ktsdi primo livello e abuild.gradle.ktsa livello di modulo.
Passaggio 1: definisci le destinazioni
Sostituisci le stringhe di route costanti con oggetti e classi @Serializable.
- Per le schermate senza argomenti: utilizza un
data object - Per le schermate con argomenti: utilizza un
data class
Prima (basato su stringa):
const val ROUTE_HOME = "home"
const val ROUTE_PROFILE = "profile/{userId}"
Dopo (type safe):
import kotlinx.serialization.Serializable
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
Passaggio 2: aggiorna la configurazione di NavHost
Aggiorna NavHost per utilizzare i nuovi tipi generici nelle funzioni composable e dialog.
Prima:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(...) }
composable("profile/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId)
}
}
Dopo:
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(...)
}
composable<Profile> { backStackEntry ->
// The library automatically handles argument extraction
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(profile.userId)
}
}
Passaggio 3: implementa chiamate di navigazione type-safe
Sostituisci le chiamate di navigazione con interpolazione di stringhe con istanze di classe.
Prima:
navController.navigate("profile/user123")
Dopo:
navController.navigate(Profile(userId = "user123"))
Passaggio 4: accesso agli argomenti nei ViewModel
Se utilizzi un ViewModel, ora puoi estrarre l'oggetto route direttamente da
SavedStateHandle.
Implementazione:
class ProfileViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
// Automatically parses arguments into the Profile class
private val profile = savedStateHandle.toRoute<Profile>()
val userId = profile.userId
}
Passaggio 5: (avanzato) gestione dei tipi personalizzati
Se devi trasmettere classi di dati complesse (non solo primitive), devi definire
un NavType personalizzato.
- Crea il tipo personalizzato:
```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)
) { ... }
Best practice e suggerimenti
- Gerarchie sigillate: per le app di grandi dimensioni, raggruppa le route utilizzando un'interfaccia o una classe sigillata per mantenere organizzata la struttura di navigazione
- Istanze oggetto: per le route senza parametri, utilizza sempre
objectanzichéclassper evitare allocazioni non necessarie - Tipi Nullable: la nuova API supporta i tipi Nullable (ad esempio,
data class Search(val query: String?)) e fornisce automaticamente i valori predefiniti - Test: utilizza
navController.currentBackStackEntry?.hasRoute<T>()per controllare la destinazione corrente in modo type-safe durante i test della UI