ViewModel en Android
disced
Posted on September 15, 2023
Índice
- Qué es
- Para qué sirve
- Beneficios de uso
- Ciclo de vida
- Implementación básica
- Implementación con DI
- ViewModel Cheatsheet
- Referencias
Qué es
El ViewModel
en Android forma parte del patrón arquitectónico Model-View-ViewModel
, siendo éste el ultimo de dicho patrón.
Es decir que es una forma de organizar el código para que sea mas entendible, mas estructurado y mas testable.
Para qué sirve
La idea fundamental es tener la parte lógica y las variables fuera de la vista (fuera del codigo de jetpack compose por ejemplo).
Por ejemplo si vamos a hacer una petición a una API HTTP u obtener datos de una base de datos, esta funcionalidad deberíamos realizarla desde el ViewModel. De esta manera conseguimos que en la vista solo se rendericen datos para que el usuario los vea, en ningún momento tenemos lógica.
El ViewModel no debe interactuar directamente con una API o una base de datos, utilizaremos el patrón Repository. Solo debe utilizar el repositorio, obtener los datos en "crudo" y realizar las modificaciones oportunas para enviarlas a la view.
Beneficios de uso
Las ventajas principales de utilizar ViewModel
en Android son:
- El ciclo de vida del ViewModel tiene mas alcance que un
Activity
Esto nos permite almacenar datos (variables) y si hay un cambio en el dispositivo, como rotación de pantalla, los datos seguirán presentes en el ViewModel. Conserva el estado de las vistas.
- Nos proporciona acceso a la lógica empresarial
Ciclo de vida
El ViewModel
no consta de un ciclo de vida tradicional como el que encontramos en las Activity
o Fragment
. En su lugar, tiene un alcance específico dentro del ciclo de vida, y este alcance es más extenso.
A diferencia del ciclo de vida de una Activity
o Fragment
, el ViewModel
ofrece un alcance más amplio. Esta particularidad nos permite conservar datos en el ViewModel
a pesar de que ocurran cambios en la interfaz, como rotaciones de pantalla o si la Activity
pasa por cambios de estado, entre otros eventos.
La siguiente imagen muestra el flujo de una Activity
y los métodos que se activan en respuesta a diversos eventos. A la derecha, se puede observar el amplio alcance del ViewModel
.
Implementación básica
En este ejemplo voy a mostrar como implementar el ViewModel
junto a Jetpack Compose
de forma básica.
También haré uso de elementos como MutableLiveData
y LiveData
(son implementaciones para clases observables, es decir que nos subscribimos a la clase y si ocurre un cambio, reaccionamos haciendo lo que sea)
Dependencias
app/build.gradle.kts
dependencies {
...
val lifecycle_version = "2.6.1"
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
}
Clase ViewModel
class GreetingViewModel : ViewModel() {
/*
* Variable (_randomvm) que observamos y podemos modificar, se usa a nivel interno en * los ViewModels */
private val _randomvm: MutableLiveData<Int> = MutableLiveData<Int>(0)
val randomvm: LiveData<Int> = _randomvm
init {
_randomvm.value = Random.nextInt(0, 2023)
}
}
En el ViewModel
hemos creado dos variables, ambas son clases observables. La única diferencia es:
-
MutableLiveData
nos permite modificar el valor que almacena. -
LiveData
no nos permite modificar el valor almacenado.
Siempre nos interesa que solo el
ViewModel
modifique los valores de las variables observables. Por este motivo utilizamosMutableLiveData
de forma privada yLiveData
de forma pública. Así solo se cambia el contenido dentro delViewModel
.
Al instanciarse la clase GreetingViewModel
se ejecutará el método init
. Éste método genera un número aleatorio y modifica el valor del MutableLiveData
. randomvm
al ser igual que MutableLiveData
, también cambiará su valor, y allá donde se observe la variable, se reaccionará a los cambios.
Composable
@Composable
fun Greeting(
name: String,
modifier: Modifier = Modifier,
viewModel: GreetingViewModel = viewModel()
) {
val randomFromVM = viewModel.randomvm
val random: Int = Random.nextInt(0, 2023)
Log.d("Random", "From ViewModel: ${randomFromVM.value}")
Log.d("Random", "From local composable's scope: $random")
Text(
text = "Hello $name!",
modifier = modifier
)
}
En el composable instanciamos el viewModel utilizando el metodo viewModel()
(que se encuentra en las dependencias instaladas en el primer paso) como argumento de la función, y dentro del composable ya podemos utilizar los métodos y atributos públicos.
Las variables que tenemos son:
-
randomFromVM
: es lo mismo que la variablerandomvm
del viewmodel -
random
: una variable en el ámbito local de la función.
Si ejecutamos el programa, vemos que cada vez que rotemos el dispositivo el valor de random
será diferente, y el valor de randomFromVM
será siempre el mismo, ya que este dato se almacena en el viewmodel y este tiene un mayor alcance.
Implementación con DI
Al utilizar inyección de dependencias con Hilt la implementación de un viewmodel es diferente, ya que deberemos proveer el ViewModel a hilt y después utilizar un método propio de hilt.
La única diferencia es que anotamos la clase con @HiltViewModel
y en el @Composable
lo inyectamos mediante el método hiltViewModel()
.
Clase ViewModel
@HiltViewModel
class GreetingViewModel @Inject constructor() : ViewModel() {
private val _randomvm: MutableLiveData<Int> = MutableLiveData<Int>(0)
val randomvm: LiveData<Int> = _randomvm
init {
_randomvm.value = Random.nextInt(0, 2023)
}
}
Composable
@Composable
fun Greeting(
name: String,
modifier: Modifier = Modifier,
viewModel: GreetingViewModel = hiltViewModel()
) {
val randomFromVM = viewModel.randomvm
val random: Int = Random.nextInt(0, 2023)
Log.d("Random", "From ViewModel: ${randomFromVM.value}")
Log.d("Random", "From local composable's scope: $random")
Text(
text = "Hello $name!",
modifier = modifier
)
}
Para implementar el viewModel junto a Hilt es necesario agregar nuevas dependencias en gradle, yo en el ejemplo no lo he hecho, pero la documentación oficial es la siguiente: Androidx Releases
ViewModel Cheatsheet
Adjunto una imagen de un cheat sheet de la documentación oficial de Android, dejo el enlace al pdf web.
Referencias
Posted on September 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 4, 2024