¿Qué pasa con SavedStateHandle y el estado de tu UI durante la muerte del proceso?
La muerte del proceso borra tu ViewModel en silencio — SavedStateHandle es el puente angosto que sobrevive, y conocer sus límites separa una respuesta senior de un encogimiento de hombros.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué pasa con SavedStateHandle y el estado de tu UI durante la muerte del proceso?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.Dos desastres muy distintos se meten en la misma bolsa de "giré la pantalla y perdí mis datos", y sólo uno de ellos es en realidad un cambio de configuración. Rotar el dispositivo, cambiar a modo oscuro o cambiar el tamaño de fuente del sistema destruye y recrea la Activity, pero el proceso sigue vivo — el sistema operativo nunca le pidió al kernel que matara nada. La muerte del proceso es el otro desastre: la app queda en segundo plano, el sistema se queda corto de memoria, y el Low Memory Killer (o su equivalente en versiones más nuevas de Android) simplemente termina el proceso para hacer lugar. Cuando el usuario vuelve a entrar desde Recientes, Android lanza un proceso completamente nuevo e intenta que parezca que no pasó nada.
#Por qué ViewModel solo no alcanza
Al ViewModel suele presentárselo como "lo que sobrevive a la rotación", y es cierto — su ViewModelStore se retiene a través de una recreación de Activity disparada por un cambio de configuración, porque es el mismo proceso el que destruye y reconstruye la capa de UI a su alrededor. La muerte del proceso elimina esa red de seguridad por completo: el proceso desaparece, así que el ViewModelStore y cada instancia de ViewModel adentro desaparecen con él. El ViewModel que se construya cuando el usuario vuelva arranca desde un constructor completamente vacío.
#El caño angosto: onSaveInstanceState y el Bundle
Antes de que una Activity sea candidata a que la maten estando en segundo plano, el sistema llama a onSaveInstanceState(Bundle), dándole una última chance de guardar una cantidad pequeña de estado. Ese Bundle viaja parcelado en una transacción Binder, y las transacciones Binder comparten un buffer de más o menos 1MB para todo el proceso — metele demasiado (una lista completa de resultados de búsqueda, un bitmap) y te aparece un TransactionTooLargeException, a veces sólo en producción, en el único dispositivo con cuarenta pestañas abiertas. Éste es el mecanismo sobre el que está construido SavedStateHandle, no algo que lo reemplaza.
#Cómo se conecta
SavedStateHandle es lo que convierte ese Bundle crudo en algo que un ViewModel puede usar con seguridad. El framework (vía SavedStateViewModelFactory, o automáticamente si usás @HiltViewModel de Hilt) le entrega al constructor del ViewModel un SavedStateHandle que ya viene poblado desde el Bundle restaurado, si lo había, y vacío si es un arranque genuinamente nuevo. Cada valor que le cargués se escribe directo en ese Bundle, así que queda listo para guardarse en el instante en que el sistema lo pida.
@HiltViewModelclass CheckoutViewModel @Inject constructor(private val savedStateHandle: SavedStateHandle,private val repository: CheckoutRepository,) : ViewModel() {// Respaldado directamente por el Bundle del SavedStateHandle — sobrevive a la muerte del proceso.val promoCode: StateFlow<String> =savedStateHandle.getStateFlow(KEY_PROMO_CODE, "")private val _uiState = MutableStateFlow(CheckoutUiState())val uiState: StateFlow<CheckoutUiState> = _uiState.asStateFlow()fun onPromoCodeChanged(code: String) {// Escribe de forma síncrona en el Bundle del handle — una muerte del// proceso una línea después igual restaura este valor exacto.savedStateHandle[KEY_PROMO_CODE] = code}init {// El carrito no es "Bundle-friendly", así que NO se restaura acá —// se vuelve a pedir, usando el promoCode restaurado como input.viewModelScope.launch {val cart = repository.loadCart(promoCode.value)_uiState.update { it.copy(cart = cart) }}}private companion object {const val KEY_PROMO_CODE = "promo_code"}}
Sólo entran tipos que puedan vivir dentro de un Bundle — primitivos, String, Parcelable, Serializable, y algunas colecciones de esos. Compose tiene su propio mecanismo hermano a nivel de composición, rememberSaveable, que usa la misma maquinaria de Bundle por debajo a través de un Saver — útil para estado que vive dentro de un solo composable y nunca necesitó un ViewModel.
#Qué pasa en la práctica, en orden
#Probarlo, no asumirlo
La respuesta que separa a un candidato senior acá no es "el ViewModel lo sobrevive" — ése es el mecanismo equivocado para el desastre equivocado, y es la respuesta incorrecta más común a esta pregunta. La respuesta senior nombra los dos modos de falla, explica por qué el ViewModel solo resuelve nada más que uno de ellos, y conoce los límites de tamaño y tipo que hacen de SavedStateHandle una capa de supervivencia delgada, no una capa de persistencia.