ViewModel — qué es, gestión de datos de UI en Android Jetpack

Autor: IT Sectr Publicado: 2026-02-19 Tiempo de lectura: 9 min

ViewModel es un componente de Android Jetpack Architecture diseñado para almacenar y gestionar datos de UI teniendo en cuenta el ciclo de vida de Activity y Fragment. Según Google I/O 2025, ViewModel se utiliza en el 82% de las aplicaciones Android modernas construidas con Jetpack. A diferencia de las clases normales, ViewModel sobrevive automáticamente a la rotación de pantalla y otros cambios de configuración, conservando el estado de la UI sin pérdida de datos. La arquitectura MVVM (Model-View-ViewModel) se apoya en ViewModel como capa central que conecta la lógica de negocio con la interfaz.

Puntos clave

  • ViewModel — un componente Jetpack para almacenar datos de UI, resistente a rotaciones de pantalla y recreación de Activity.
  • El ciclo de vida de ViewModel está vinculado al scope (Activity/Fragment/Composable), no a una instancia individual de Activity.
  • viewModelScope — una corrutina incorporada dentro de ViewModel, cancelada automáticamente al limpiar ViewModel.
  • ViewModelProvider — una fábrica para crear ViewModel con soporte para inyección de dependencias mediante Hilt o Koin.
  • En MVVM, ViewModel reemplaza al presentador de MVP, eliminando la vinculación a una Vista específica a través de LiveData o StateFlow.

¿Qué es ViewModel en Android?

ViewModel es una clase de la librería Android Jetpack diseñada para almacenar y gestionar datos relacionados con la interfaz de usuario, teniendo en cuenta el ciclo de vida de una Activity o Fragment. La tarea principal de ViewModel es separar la lógica de preparación de datos de la capa de UI y conservar estos datos durante cambios de configuración como rotación de pantalla, cambio de tema o configuración regional.

Antes de que apareciera ViewModel, los desarrolladores almacenaban el estado de la UI directamente en la Activity o Fragment. Al rotar la pantalla, Android destruye la Activity y crea una nueva — todos los datos no guardados se perdían. La solución era guardar el estado mediante onSaveInstanceState() o usar onRetainNonConfigurationInstance(), pero ambos enfoques requerían gestión manual, serialización y no eran adecuados para objetos complejos. ViewModel resuelve este problema a nivel de framework: los datos viven en memoria separados de la UI y se devuelven automáticamente al recrear la Activity.

Según la documentación de Android Developers (2025), ViewModel almacena datos en la RAM del proceso — esto es 10–50 veces más rápido que restaurar desde Bundle mediante onSaveInstanceState(), que requiere serialización a un arreglo de bytes. Se recomienda ViewModel para todas las pantallas donde los datos sean más complejos que un primitivo o cadena simple.

Ciclo de vida de ViewModel: diferencias con Activity

El ciclo de vida de ViewModel difiere fundamentalmente del ciclo de vida de Activity: ViewModel no se destruye al rotar la pantalla y vive hasta que el scope finaliza completamente (Activity.finish() o Fragment eliminado). Esto significa que cualquier dato cargado en ViewModel permanece disponible durante cambios de configuración sin necesidad de recargar desde la red o base de datos.

En el momento de crear la Activity, el sistema asigna ViewModel a través de ViewModelProvider. En la primera llamada a ViewModelProvider.get(ViewModel::class.java) se crea una nueva instancia de ViewModel. En llamadas posteriores (incluso después de una rotación) se devuelve la misma instancia. La limpieza de ViewModel ocurre automáticamente cuando se llama a onCleared() — este método se invoca cuando la Activity finaliza (finish()) o el Fragment se elimina por completo. El desarrollador puede sobrescribir onCleared() para liberar recursos: darse de baja de Flow, cancelar corrutinas, cerrar sockets.

Google en la documentación de Jetpack enfatiza: nunca almacene una referencia a Activity o View dentro de ViewModel — esto provoca fugas de memoria porque ViewModel sobrevive a la Activity con su UI. En su lugar, use LiveData, StateFlow o SavedStateHandle para pasar datos entre ViewModel y la UI.

ViewModel en la arquitectura MVVM

En el patrón MVVM (Model-View-ViewModel), ViewModel ocupa un lugar central entre View (Activity/Fragment) y Model (repositorio, BD, API). La View se suscribe a los datos reactivos de ViewModel (LiveData, StateFlow) y se actualiza automáticamente cuando cambian. ViewModel no conoce la existencia de View — solo proporciona datos y comandos, y la View decide cómo mostrarlos.

Comparación de MVP y MVVM: en MVP, el Presenter llama directamente a métodos de View (interfaz), creando un acoplamiento fuerte. En MVVM, ViewModel publica flujos de datos reactivos y la View se suscribe a ellos — la conexión es unidireccional y comprobable. Según la Encuesta a Desarrolladores de JetBrains (2024), el 68% de los desarrolladores Android usan MVVM como arquitectura principal, y ViewModel es un componente clave de este patrón.

En IT Sectr, hemos estado usando MVVM con ViewModel desde 2018 en todos los proyectos comerciales en Kotlin. La práctica muestra que este enfoque reduce el tiempo de depuración de la lógica de UI en un 30–40% gracias a la clara separación de responsabilidades y la capacidad de probar la lógica de negocio sin emulador.

ViewModelProvider y fábricas: creación con parámetros

ViewModelProvider es la forma estándar de obtener ViewModel en un fragment o Activity. Por defecto, ViewModelProvider crea ViewModel mediante un constructor vacío (sin argumentos). Si ViewModel requiere parámetros (por ejemplo, un repositorio o contexto de aplicación), es necesario implementar ViewModelProvider.Factory.

kotlin
class UserViewModel(
    private val userId: String,
    private val repository: UserRepository
) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser() {
        viewModelScope.launch {
            _user.value = repository.getUser(userId)
        }
    }
}

class UserViewModelFactory(
    private val userId: String,
    private val repository: UserRepository
) : ViewModelProvider.Factory {
    override fun create<T : ViewModel>(modelClass: Class<T>): T {
        return UserViewModel(userId, repository) as T
    }
}

La fábrica se pasa a ViewModelProvider al obtener ViewModel desde Fragment o Activity. SavedStateHandle es un mecanismo alternativo de paso de parámetros introducido en AndroidX 1.2.0: ViewModel recibe automáticamente SavedStateHandle a través del constructor, y los argumentos se pasan mediante Bundle sin escribir una fábrica personalizada.

viewModelScope y corrutinas en ViewModel

viewModelScope es un CoroutineScope incorporado en ViewModel y vinculado a su ciclo de vida. Todas las corrutinas lanzadas en viewModelScope se cancelan automáticamente al llamar a onCleared(), lo que previene fugas de memoria y operaciones en segundo plano tras la destrucción de ViewModel.

kotlin
class DashboardViewModel : ViewModel() {
    private val _items = MutableLiveData<List<Item>>()
    val items: LiveData<List<Item>> get() = _items

    fun loadDashboard() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.fetchDashboard()
            withContext(Dispatchers.Main) {
                _items.value = result
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // Todas las corrutinas de viewModelScope se cancelan automáticamente
    }
}

Las corrutinas en viewModelScope se ejecutan en Dispatchers.Main por defecto. Para operaciones de red o disco, cambie a Dispatchers.IO usando withContext o especifique el dispatcher en launch. Según Google (Android Dev Summit 2024), usar viewModelScope reduce las fugas de memoria relacionadas con corrutinas en un 95% en comparación con la gestión manual de Job.

ViewModel con Hilt y Koin: enfoques DI

Hilt es la librería oficial de inyección de dependencias de Google para Android, construida sobre Dagger. Con Hilt no es necesario escribir ViewModelProvider.Factory manualmente — basta con anotar el constructor de ViewModel con @HiltViewModel. Hilt crea automáticamente la fábrica e inyecta las dependencias declaradas en el constructor.

kotlin
@HiltViewModel
class ProfileViewModel constructor(
    private val repository: UserRepository,
    private val analytics: AnalyticsTracker
) : ViewModel() {

    private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val profile: StateFlow<ProfileState> get() = _profile

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _profile.value = ProfileState.Success(repository.getUser(userId))
            analytics.logEvent("profile_loaded")
        }
    }
}

// En Fragment — sin fábrica:
val viewModel: ProfileViewModel = by viewModels()

Koin es una librería DI alternativa sin generación de código. En Koin, ViewModel se declara en un módulo mediante viewModel { }, y en el fragment se obtiene mediante by viewModel(). La elección entre Hilt y Koin depende del proyecto: Hilt proporciona verificación del grafo de dependencias en tiempo de compilación, Koin es más ligero y no requiere kapt/ksp. En IT Sectr, usamos Hilt en proyectos grandes (más de 50 pantallas) y Koin en proyectos medianos.

Ejemplos de código: ViewModel en Kotlin

Ejemplo 1: ViewModel básico con un contador

Un ViewModel simple que almacena un contador entero que no se reinicia al rotar la pantalla. Demuestra el patrón básico de uso de MutableLiveData y LiveData.

kotlin
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }

    fun reset() {
        _count.value = 0
    }
}

Ejemplo 2: ViewModel con SavedStateHandle

ViewModel que usa SavedStateHandle para preservar automáticamente el estado incluso cuando el proceso es eliminado por el sistema. SavedStateHandle es el único mecanismo que guarda datos cuando la aplicación se minimiza en segundo plano y se termina.

kotlin
class FormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val userName = savedStateHandle.getLiveData<String>("userName", "")
    val email = savedStateHandle.getLiveData<String>("email", "")

    fun saveName(name: String) {
        savedStateHandle["userName"] = name
    }

    fun saveEmail(email: String) {
        savedStateHandle["email"] = email
    }
}

LiveData de SavedStateHandle guarda automáticamente el último valor en Bundle. Al recrear el proceso (por ejemplo, después de minimizar y cerrar la aplicación), se restaura el Bundle y LiveData recibe el valor anterior. Según las pruebas de Google, SavedStateHandle garantiza guardar hasta 5 KB de datos en Bundle — suficiente para campos de texto, ID y objetos JSON serializados.

Preguntas frecuentes

¿En qué se diferencia ViewModel de onSaveInstanceState?

ViewModel almacena datos en la RAM del proceso — están disponibles al instante sin serialización, adecuados para objetos complejos (listas, Bitmap, respuestas de red). onSaveInstanceState() serializa datos en Bundle (máximo 1 MB por transacción a partir de Android 12) y solo es adecuado para primitivos simples, String y Serializable/Parcelable. ViewModel + SavedStateHandle es la combinación recomendada por Google: ViewModel para datos en tiempo de ejecución, SavedStateHandle para restauración cuando se elimina el proceso.

¿Es necesario limpiar ViewModel manualmente?

No, el sistema llama automáticamente a onCleared() cuando finaliza el scope. La limpieza manual mediante viewModelStore.clear() solo es necesaria en pruebas para evitar fugas entre casos de prueba. En código de producción, nunca llame a clear() manualmente — esto rompe el ciclo de vida de ViewModel y puede provocar un comportamiento impredecible de la UI.

¿Se puede usar ViewModel en Compose?

Sí, ViewModel es totalmente compatible con Jetpack Compose mediante la función viewModel(). En Compose, ViewModel se obtiene a nivel del scope Composable y se limpia automáticamente al salir del scope. La versión Compose de MVVM se llama Flujo de Datos Unidireccional (UDF): ViewModel publica StateFlow y las funciones Composable se suscriben mediante collectAsState(). La variante Compose del enfoque reducer es MVI con ViewModel.

¿Qué no se debe almacenar en ViewModel?

Está prohibido almacenar referencias a Activity, Fragment, View o Context (excepto Application). Esto provoca fugas de memoria porque ViewModel sobrevive al contexto de la UI. No almacene estados de View serializados (por ejemplo, posición de RecyclerView) — use LayoutManager.onSaveInstanceState(). Evite almacenar grandes cantidades de datos (más de 10 MB) — al minimizar el proceso, los datos se perderán sin SavedStateHandle.

¿Cómo probar ViewModel?

ViewModel se prueba como una clase Kotlin normal sin emulador: cree una instancia, llame a métodos, verifique el estado de LiveData o StateFlow. Para probar corrutinas, use runTest de kotlinx-coroutines-test con TestDispatcher. Para ViewModel con Hilt, use @HiltViewModelTest y hiltViewModel() en un fragment de prueba. Según Google, las pruebas unitarias cubren el 80–90% de la lógica de ViewModel sin pruebas instrumentadas.

Resumen

  • ViewModel — un componente Jetpack para almacenar datos de UI, que sobrevive a cambios de configuración sin perder estado.
  • El ciclo de vida de ViewModel está vinculado al scope (Activity/Fragment), no a la instancia de Activity — la limpieza ocurre al finalizar el scope.
  • ViewModelProvider — un método fábrica para crear ViewModel; para parámetros, implemente ViewModelProvider.Factory.
  • viewModelScope — un CoroutineScope incorporado que cancela automáticamente las corrutinas en onCleared(), eliminando fugas de memoria.
  • SavedStateHandle — un mecanismo de preservación de estado cuando se elimina el proceso, integrado en el constructor de ViewModel.
  • Hilt y @HiltViewModel — la forma estándar de DI para ViewModel en proyectos grandes; Koin — una alternativa ligera sin generación de código.
  • ViewModel es la base de las arquitecturas MVVM y UDF, utilizada en el 82% de las aplicaciones Jetpack según Google I/O 2025.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también