ViewModel en Android

dragosb

disced

Posted on September 15, 2023

ViewModel en Android

Índice


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.

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

Link oficial

app/build.gradle.kts



dependencies {
    ...
    val lifecycle_version = "2.6.1"  
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
}


Enter fullscreen mode Exit fullscreen mode

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)  
    }  
}


Enter fullscreen mode Exit fullscreen mode

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 utilizamos MutableLiveData de forma privada y LiveData de forma pública. Así solo se cambia el contenido dentro del ViewModel.

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  
    )  
}


Enter fullscreen mode Exit fullscreen mode

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 variable randomvm 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.

Salida logcat

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)  
    }  
}



Enter fullscreen mode Exit fullscreen mode

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  
    )  
}


Enter fullscreen mode Exit fullscreen mode

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.

Viewmodel Cheatsheet

Referencias

Descripción general de ViewModel  |  Android Developers

ViewModel te permite administrar los datos de tu IU de forma optimizada para ciclos de vida.

favicon developer.android.com
💖 💪 🙅 🚩
dragosb
disced

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