Descripción general de ViewModel Parte de Android Jetpack.
Se diseñó la clase ViewModel
a fin de almacenar y administrar datos relacionados con la IU de manera optimizada para los ciclos de vida. La clase ViewModel
permite que se conserven los datos luego de cambios de configuración, como las rotaciones de pantallas.
El framework de Android administra el ciclo de vida de los controladores de IU, como las actividades y los fragmentos. El framework podría decidir destruir o recrear un controlador de IU como respuesta a acciones del usuario o eventos del dispositivo determinados que están completamente fuera de tu control.
Si el sistema destruye o recrea un controlador de IU, se perderán todos los datos relacionados con la IU que almacenes en el controlador. Por ejemplo, tu app podría incluir una lista de usuarios en una de sus actividades. Cuando se recrea la actividad para un cambio de configuración, la actividad nueva tiene que volver a recuperar la lista de usuarios. En el caso de los datos simples, la actividad puede usar el método onSaveInstanceState()
y restablecer sus datos a partir del paquete en onCreate()
, aunque este enfoque solo es apto para pequeñas cantidades de datos que se pueden serializar y deserializar, no para cantidades de datos posiblemente grandes, como una lista de usuarios o mapas de bits.
Otro problema es que los controladores de IU frecuentemente necesitan hacer llamadas asíncronas que podrían tardar en devolverse. El controlador de IU necesita administrar estas llamadas y garantizar que el sistema las borre después de su destrucción para evitar potenciales pérdidas de memoria. Esta administración requiere mucho mantenimiento y, en los casos en que se recrea el objeto para un cambio de configuración, es una pérdida de recursos, ya que el objeto quizás deba volver a emitir llamadas que ya hizo.
Los controladores de IU, como las actividades y los fragmentos, tienen como objetivo principal mostrar datos de IU, reaccionar a las acciones de los usuarios o administrar la comunicación del sistema operativo, como las solicitudes de permisos. Si se establece que los controladores de IU también sean responsables de cargar datos de una red o base de datos, la clase estará sobrecargada. Asignar demasiadas responsabilidades a los controladores de IU puede provocar que una sola clase trate de administrar todo el trabajo de una app por su cuenta, en lugar de delegar el trabajo a otras clases. Si asignas demasiadas responsabilidades a los controladores de IU de este modo, la prueba también se dificulta considerablemente.
Es más fácil y eficiente separar la propiedad de los datos de visualización de la lógica del controlador de IU.
Cómo implementar un ViewModel
Los componentes de arquitectura proporcionan una clase de ayuda de ViewModel
para el controlador de IU que es responsable de preparar los datos de la IU.
Durante los cambios de configuración, se retienen automáticamente los objetos ViewModel
, de manera que los datos que contienen estén disponibles de inmediato para la siguiente instancia de actividad o fragmento. Por ejemplo, si necesitas mostrar una lista de usuarios en tu app, asegúrate de asignar la responsabilidad de adquirir y mantener la lista de usuarios a un ViewModel
, en lugar de una actividad o un fragmento, como se muestra en el siguiente código de muestra:
Vistas
class MyViewModel : ViewModel() { private val users: MutableLiveData<List<User>> by lazy { MutableLiveData<List<User>>().also { loadUsers() } } fun getUsers(): LiveData<List<User>> { return users } private fun loadUsers() { // Do an asynchronous operation to fetch users. } }
Vistas
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
Luego, puedes acceder a la lista desde una actividad de la siguiente manera:
Vistas
class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact val model: MyViewModel by viewModels() model.getUsers().observe(this, Observer<List<User>>{ users -> // update UI }) } }
Vistas
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } }
Si se vuelve a crear la actividad, recibe la misma instancia de MyViewModel
que creó la primera actividad. Cuando finaliza la actividad del propietario, el framework llama al método onCleared()
de los objetos ViewModel
para que borre los recursos.
Los objetos ViewModel
están diseñados para sobrevivir a instancias específicas de vistas o LifecycleOwners
. Este diseño también te permite escribir pruebas para abarcar un ViewModel
con mayor facilidad, ya que no sabe acerca de los objetos de vista y Lifecycle
.
Los objetos ViewModel
pueden contener LifecycleObservers
, como objetos LiveData
. Sin embargo, los objetos ViewModel
no deben observar cambios en los elementos observables optimizados para ciclos de vida, como los objetos LiveData
.
Si el ViewModel
necesita el contexto de Application
, por ejemplo, para buscar un servicio del sistema, puede extender la clase AndroidViewModel
y hacer que un constructor reciba la Application
, ya que la clase Application
extiende Context
.
Cómo crear ViewModels con dependencias
Según las prácticas recomendadas para insertar dependencias, los ViewModels pueden tomar dependencias como parámetros en su constructor. En su mayoría, son de los tipos de capas de dominio o datos. Debido a que el framework proporciona los ViewModels, se requiere un mecanismo especial para crear instancias de ellos. Ese mecanismo es la interfaz ViewModelProvider.Factory
. Solo las implementaciones de esta interfaz pueden crear instancias de ViewModels en el alcance correcto.
Si una clase ViewModel recibe dependencias en su constructor, proporciona una fábrica que implemente la interfaz ViewModelProvider.Factory
. Anula la función create(Class<T>, CreationExtras)
para proporcionar una instancia nueva del ViewModel.
CreationExtras
te permite acceder a información relevante que te ayuda a crear una instancia de ViewModel. Esta es una lista de claves a las que se puede acceder desde los extras:
Clave | Funcionalidad |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY |
Esta Key proporciona acceso a la clave personalizada que pasaste a ViewModelProvider.get() . |
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY |
Proporciona acceso a la instancia de la clase Application . |
SavedStateHandleSupport.DEFAULT_ARGS_KEY |
Proporciona acceso al paquete de argumentos que debes usar para construir un SavedStateHandle . |
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY |
Proporciona acceso al SavedStateRegistryOwner que se usa para construir el ViewModel . |
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY |
Proporciona acceso al ViewModelStoreOwner que se usa para construir el ViewModel . |
Para crear una instancia nueva de SavedStateHandle
, usa la función CreationExtras.createSavedStateHandle()
.createSavedStateHandle()) y pásala al ViewModel.
A continuación, se muestra un ejemplo de a fin de proporcionar una instancia de un ViewModel que toma un repositorio definido para la clase Application
y SavedStateHandle
como dependencias:
Vistas
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // ... // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( modelClass: Class<T>, extras: CreationExtras ): T { // Get the Application object from extras val application = checkNotNull(extras[APPLICATION_KEY]) // Create a SavedStateHandle for this ViewModel from extras val savedStateHandle = extras.createSavedStateHandle() return MyViewModel( (application as MyApplication).myRepository, savedStateHandle ) as T } } } }
Vistas
import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; import androidx.lifecycle.viewmodel.ViewModelInitializer; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>( MyViewModel.class, creationExtras -> { MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY); assert app != null; SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras); return new MyViewModel(app.getMyRepository(), savedStateHandle); } ); }
Luego, puedes usar esta fábrica cuando recuperas una instancia del ViewModel:
Vistas
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.Factory } // Rest of Activity code }
Vistas
import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, ViewModelProvider.Factory.from(MyViewModel.initializer) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory) ) { // ... }
Como alternativa, puedes usar el DSL de fábrica de ViewModel para crear fábricas con una API de Kotlin más idiomática:
Vistas
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { val savedStateHandle = createSavedStateHandle() val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository MyViewModel( myRepository = myRepository, savedStateHandle = savedStateHandle ) } } } }
Fábricas para una versión de ViewModel anterior a la 2.5.0
Si usas una versión de ViewModel anterior a la 2.5.0, debes proporcionar fábricas de un subconjunto de clases que extiendan ViewModelProvider.Factory
e implementar la función create(Class<T>)
. Según las dependencias que necesite ViewModel, se debe extender una clase diferente:
- Desde
AndroidViewModelFactory
si se necesita la claseApplication
. - Desde
AbstractSavedStateViewModelFactory
si se debe pasarSavedStateHandle
como dependencia.
Si Application
o SavedStateHandle
no son necesarios, solo debes extenderlos desde ViewModelProvider.Factory
.
En el siguiente ejemplo, se usa un AbstractSavedStateViewModelFactory
para un ViewModel que toma un repositorio y un tipo SavedStateHandle
como dependencia:
Vistas
class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic ... // Define ViewModel factory in a companion object companion object { fun provideFactory( myRepository: MyRepository, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, ): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { return MyViewModel(myRepository, handle) as T } } } }
Vistas
import androidx.annotation.NonNull; import androidx.lifecycle.AbstractSavedStateViewModelFactory; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } } public class MyViewModelFactory extends AbstractSavedStateViewModelFactory { private final MyRepository myRepository; public MyViewModelFactory( MyRepository myRepository ) { this.myRepository = myRepository; } @SuppressWarnings("unchecked") @NonNull @Override protected <T extends ViewModel> T create( @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle ) { return (T) new MyViewModel(myRepository, handle); } }
Luego, puedes usar fábrica para recuperar tu ViewModel:
Vistas
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.provideFactory((application as MyApplication).myRepository, this) } // Rest of Activity code }
Vistas
public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, new MyViewModelFactory(((MyApplication) getApplication()).getMyRepository()) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel( factory = MyViewModel.provideFactory( (LocalContext.current.applicationContext as MyApplication).myRepository, owner = LocalSavedStateRegistryOwner.current ) ) ) { // ... }
El ciclo de vida de un ViewModel
El alcance de los objetos ViewModel
se determina según el Lifecycle
que se pasa al ViewModelProvider
cuando se recibe el ViewModel
. El ViewModel
permanece en la memoria hasta que el Lifecycle
que determina su alcance desaparece de forma permanente. En el caso de una actividad, cuando termina; en el caso de un fragmento, cuando se desconecta.
La figura 1 muestra los estados del ciclo de vida de una actividad a medida que atraviesa una rotación y hasta que termina. La ilustración también muestra el ciclo de vida del ViewModel
junto al de la actividad asociada. Este diagrama en particular muestra los estados de una actividad. Los mismos estados básicos se aplican al ciclo de vida de un fragmento.
Por lo general, solicitas un ViewModel
la primera vez que el sistema llama al método onCreate()
del objeto de una actividad. El sistema puede llamar a onCreate()
varias veces durante la vida de una actividad, como cuando rota la pantalla de un dispositivo. El ViewModel
existe desde la primera vez que solicitas un ViewModel
hasta que finaliza la actividad y se destruye.
Cómo compartir los datos entre fragmentos
Es muy común que dos o más fragmentos en una actividad necesiten comunicarse entre sí. Imagina un caso común de fragmentos de vista dividida (list-detail
), en el que tienes un fragmento donde el usuario selecciona un elemento de una lista y otro fragmento que muestra el contenido del elemento seleccionado. Este caso nunca es banal, ya que ambos fragmentos necesitan definir una parte de la descripción de la interfaz y la actividad del propietario debe vincularlos. Además, los dos fragmentos deben manejar la situación en la que el otro fragmento todavía no se haya creado o no esté visible.
Se puede abordar este punto problemático común usando objetos ViewModel
. Esos fragmentos pueden compartir un ViewModel
mediante su alcance de actividad para administrar esta comunicación, como se indica en el siguiente código de muestra:
Vistas
class SharedViewModel : ViewModel() { val selected = MutableLiveData- () fun select(item: Item) { selected.value = item } } class ListFragment : Fragment() { private lateinit var itemSelector: Selector // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) itemSelector.setOnClickListener { item -> // Update the UI } } } class DetailFragment : Fragment() { // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.selected.observe(viewLifecycleOwner, Observer
- { item -> // Update the UI }) } }
Vistas
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData- getSelected() { return selected; } } public class ListFragment extends Fragment { private SharedViewModel model; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); model.getSelected().observe(getViewLifecycleOwner(), item -> { // Update the UI. }); } }
Ten en cuenta que ambos fragmentos recuperan la actividad que los contiene. De esa manera, cuando cada fragmento obtiene el ViewModelProvider
, reciben la misma instancia de SharedViewModel
, cuyo alcance está determinado por esta actividad.
Este enfoque ofrece las siguientes ventajas:
- La actividad no necesita hacer nada ni saber sobre esta comunicación.
- Los fragmentos no necesitan saber acerca del otro, excepto por el contrato de
SharedViewModel
. Si uno de los fragmentos desaparece, el otro sigue funcionando como de costumbre. - Cada fragmento tiene su propio ciclo de vida y no se ve afectado por el ciclo de vida del otro. Si un fragmento reemplaza al otro, la IU continúa funcionando sin problemas.
Cómo reemplazar cargadores con ViewModel
Con frecuencia, se usan las clases de cargador como CursorLoader
para mantener los datos en la IU de una app sincronizados con una base de datos. Puedes usar ViewModel
, con algunas otras clases, para reemplazar el cargador. Usar un ViewModel
te permite separar el controlador de IU de la operación de carga de datos. De esa forma, tienes menos referencias pesadas entre clases.
En un enfoque común con respecto al uso de cargadores, una app podría usar un CursorLoader
para observar el contenido de una base de datos. Cuando un valor en la base de datos cambia, el cargador activa automáticamente una nueva carga de los datos y actualiza la IU.

ViewModel
funciona con Room y LiveData para reemplazar el cargador.
El ViewModel
garantiza que los datos sobrevivan al cambio de configuración del dispositivo.
Room informa sobre tu LiveData
cuando cambia la base de datos, y LiveData, a su vez, actualiza la IU con los datos revisados.

Cómo usar corrutinas con ViewModel
ViewModel
incluye compatibilidad con corrutinas de Kotlin. Para obtener más información, consulta Cómo usar corrutinas de Kotlin con componentes de la arquitectura de Android.
Más información
A medida que los datos se hacen más complejos, quizás decidas tener una clase por separado solo para cargar los datos. El objetivo de ViewModel
es encapsular los datos para un controlador de IU a fin de que estos sobrevivan a los cambios de configuración. Si quieres obtener más información para cargar, conservar y administrar datos durante cambios de configuración, consulta Cómo guardar estados de IU.
La Guía para la arquitectura de apps de Android sugiere crear una clase de repositorio para administrar estas funciones.
Recursos adicionales
Para obtener más información sobre la clase ViewModel
, consulta los siguientes recursos.
Ejemplos
- Muestra básica de los componentes de la arquitectura Android
- Sunflower, una app de jardinería que ilustra las prácticas recomendadas de desarrollo de Android con Android Jetpack
Codelabs
- Android Room con una vista (Java) (Kotlin)
- Codelab de componentes que tienen en cuenta el ciclo de vida de Android
Blogs
- ViewModels: un ejemplo simple
- ViewModels: Persistence, onSaveInstanceState(), restauración del estado de IU y cargadores
- ViewModels y LiveData: patrones y antipatrones
- Desmitificación de Kotlin: explicación breve de la sintaxis de Lambda
- Desmitificación de Kotlin: funciones del alcance
- Desmitificación de Kotlin: cuándo usar accesos personalizados
- Carga de datos teniendo en cuenta el ciclo de vida con componentes de arquitectura