Después de dos o más rotaciones de pantalla, lifecycleScope.launchWhenCreated deja de funcionar como se espera.
Tengo un código como este:
private val appViewModel: AppViewModel by activityViewModels()
private lateinit var user: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Esto establece la variable user en el valor recopilado de un StateFlow desde appViewModel
lifecycleScope.launchWhenCreated {
appViewModel.user.collect { flowUser -> user = flowUser }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Este método utiliza la variable lateinit user
lifecycleScope.launchWhenStarted {
doThingWithUser()
}
return binding?.root
}
Desde el valor de *StateFlow* persiste incluso después de ser recopilado, después de que se rota la pantalla, se llama a
lifecycleScope.launchWhenCreated, se recopila nuevamente el **flowUser** del *flow* , se le asigna a la variable lateinit user y luego se llama a
doThingWithUser` y todo funciona correctamente.
Pero después de dos o más rotaciones, esto deja de ser cierto, por alguna razón user no se inicializa, se llama a doThingWithUser y la aplicación se bloquea con kotlin.UninitializedPropertyAccessException.
¿Qué estoy haciendo mal? ¿El valor de StateFlow desaparece después de dos recopilaciones o rotaciones de pantalla? ¿Algo sucede con el flow real dentro del ViewModel? ¿Algo sucede con los métodos onCreate y onCreateView? ¿O launchWhenStarted y launchWhenCreated se comportan de manera diferente después de dos rotaciones?
Gracias.
davy.ai
El problema con el código está relacionado con el comportamiento del lifecycleScope en conjunto con el ViewModel que se está utilizando, específicamente con el StateFlow que se está utilizando.
Cuando ocurre un cambio de configuración (como una rotación de pantalla), la Activity se recrea y se crea una nueva instancia del ViewModel, lo que hace que el StateFlow emita su valor actual nuevamente. Esto significa que el código dentro del bloque
lifecycleScope.launchWhenCreated
se ejecutará nuevamente y se recogerá el valor, pero como la variableuser
ya ha sido inicializada, no se volverá a asignar, lo que provoca la excepción kotlin.UninitializedPropertyAccessException cuando se llama a la funcióndoThingWithUser
más tarde.Para solucionar este problema, puedes utilizar el SavedStateHandle proporcionado por el ViewModel para almacenar y recuperar el valor de la variable
user
. Esto te permitirá persistir el valor a través de cambios de configuración.Aquí tienes una versión actualizada del código que utiliza el SavedStateHandle:
En este código actualizado, recuperamos el valor de la variable
user
del SavedStateHandle si existe, o del StateFlow si el estado guardado es nulo. También actualizamos el estado guardado cada vez que el StateFlow emite un nuevo valor.Además, guardamos el valor de la variable
user
en el Bundle en el métodoonSaveInstanceState
, para que se pueda recuperar nuevamente cuando se recrea el Fragment.