En este tutorial, discutiremos e implementaremos el Patrón Arquitectónico MVVM en nuestra Aplicación Android. Anteriormente hemos discutido el Patrón MVP de Android.
¿Por qué necesitamos estos patrones? Agregar todo en una sola Actividad o Fragmento conduciría a problemas en las pruebas y refactorización del código. Por lo tanto, se recomienda el uso de la separación del código y una arquitectura limpia.
Android MVVM
MVVM significa Modelo, Vista, ViewModel.
- Modelo: Contiene los datos de la aplicación. No puede comunicarse directamente con la Vista. Generalmente, se recomienda exponer los datos al ViewModel a través de Observables.
- Vista: Representa la interfaz de usuario de la aplicación sin ninguna lógica de aplicación. Observa el ViewModel.
- ViewModel: Actúa como un enlace entre el Modelo y la Vista. Es responsable de transformar los datos del Modelo. Proporciona flujos de datos a la Vista. También utiliza ganchos o devoluciones de llamada para actualizar la Vista. Solicitará los datos del Modelo.
El siguiente flujo ilustra el patrón MVVM central. ¿En qué se diferencia esto de MVP?
- ViewModel reemplaza al Presentador en la capa intermedia.
- El Presentador mantiene referencias a la Vista. El ViewModel no lo hace.
- El Presentador actualiza la Vista utilizando el método clásico (desencadenando métodos).
- El ViewModel envía flujos de datos.
- El Presentador y la Vista tienen una relación de 1 a 1.
- La Vista y el ViewModel tienen una relación de 1 a muchos.
- El ViewModel no sabe que la Vista está escuchándolo.
Hay dos formas de implementar MVVM en Android:
- Vinculación de datos
- RXJava
En este tutorial, solo usaremos la Vinculación de datos. La Biblioteca de Vinculación de Datos fue introducida por Google para vincular datos directamente en el diseño xml. Para obtener más información sobre la Vinculación de Datos, consulta este tutorial. Crearemos una aplicación de ejemplo de una página de inicio de sesión simple que solicita la entrada del usuario. Veremos cómo el ViewModel notifica a la Vista cuándo mostrar un mensaje Toast sin mantener una referencia de la Vista.
¿Cómo es posible notificar a una clase sin tener una referencia de ella? Se puede hacer de tres formas diferentes:
- Usando Vinculación de Datos Bidireccional
- Usando Live Data
- Usando RxJava
Enlace de datos bidireccional
El enlace de datos bidireccional es una técnica de vinculación de objetos a diseños XML de manera que el objeto y el diseño puedan enviar datos entre sí. En nuestro caso, el ViewModel puede enviar datos al diseño y también observar cambios. Para esto, necesitamos un BindingAdapter
y un atributo personalizado definido en el XML. El Adaptador de Vinculación escucharía los cambios en la propiedad del atributo. Aprenderemos más sobre el enlace de datos bidireccional a través de nuestro ejemplo a continuación.
Estructura del Proyecto de Ejemplo MVVM de Android
Agregando la Biblioteca de Enlace de Datos
Agregue el siguiente código al archivo build.gradle de su aplicación:
android {
dataBinding {
enabled = true
}
}
Esto habilita el Enlace de Datos en su aplicación.
Agregando las Dependencias
Agrega las siguientes dependencias en tu archivo build.gradle
:
implementation 'android.arch.lifecycle:extensions:1.1.0'
Modelo
El modelo contendría el correo electrónico y la contraseña del usuario. La siguiente clase User.java lo hace:
package com.journaldev.androidmvvmbasics.model;
public class User {
private String email;
private String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}
La vinculación de datos bidireccional nos permite vincular objetos en los diseños XML de manera que el objeto pueda enviar datos al diseño, y viceversa. La sintaxis para la vinculación de datos bidireccional es @={variable}
Diseño
El código para el archivo activity_main.xml se muestra a continuación:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:bind="https://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:orientation="vertical">
<EditText
android:id="@+id/inEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress"
android:padding="8dp"
android:text="@={viewModel.userEmail}" />
<EditText
android:id="@+id/inPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
android:padding="8dp"
android:text="@={viewModel.userPassword}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:onClick="@{()-> viewModel.onLoginClicked()}"
android:text="LOGIN"
bind:toastMessage="@{viewModel.toastMessage}" />
</LinearLayout>
</ScrollView>
</layout>
La Vinculación de Datos requiere que configuremos la etiqueta de diseño en la parte superior. Aquí nuestro ViewModel vincula los datos a la Vista. ()-> viewModel.onLoginClicked()
invoca el oyente de clic del botón lambda definido en nuestro ViewModel. El EditText actualiza los valores en el Modelo (a través de ViewModel). bind:toastMessage="@{viewModel.toastMessage}"
es un atributo personalizado que hemos creado para la vinculación de datos bidireccional. Según los cambios en toastMessage en el ViewModel, el BindingAdapter se activaría en la Vista.
ViewModel
El código para LoginViewModel.java se muestra a continuación:
package com.journaldev.androidmvvmbasics.viewmodels;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.text.TextUtils;
import android.util.Patterns;
import com.android.databinding.library.baseAdapters.BR;
import com.journaldev.androidmvvmbasics.model.User;
public class LoginViewModel extends BaseObservable {
private User user;
private String successMessage = "Login was successful";
private String errorMessage = "Email or Password not valid";
@Bindable
private String toastMessage = null;
public String getToastMessage() {
return toastMessage;
}
private void setToastMessage(String toastMessage) {
this.toastMessage = toastMessage;
notifyPropertyChanged(BR.toastMessage);
}
public void setUserEmail(String email) {
user.setEmail(email);
notifyPropertyChanged(BR.userEmail);
}
@Bindable
public String getUserEmail() {
return user.getEmail();
}
@Bindable
public String getUserPassword() {
return user.getPassword();
}
public void setUserPassword(String password) {
user.setPassword(password);
notifyPropertyChanged(BR.userPassword);
}
public LoginViewModel() {
user = new User("","");
}
public void onLoginClicked() {
if (isInputDataValid())
setToastMessage(successMessage);
else
setToastMessage(errorMessage);
}
public boolean isInputDataValid() {
return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches() && getUserPassword().length() > 5;
}
}
Los métodos que se llaman en el diseño están implementados en el código anterior con la misma firma. Si el homólogo XML del método no existe, necesitamos cambiar el atributo a app:
. La clase anterior también puede extender ViewModel. Pero necesitamos BaseObservable ya que convierte los datos en flujos y notifica cuando la propiedad toastMessage
cambia. Necesitamos definir el getter y setter para el atributo personalizado toastMessage definido en el XML. Dentro del setter, notificamos al observador (que será la Vista en nuestra aplicación) que los datos han cambiado. La Vista (Nuestra actividad) puede definir la acción apropiada.
La clase BR se genera automáticamente a partir de la vinculación de datos cuando reconstruyes el proyecto
El código para la clase MainActivity.java
se muestra a continuación:
package com.journaldev.androidmvvmbasics.views;
import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.journaldev.androidmvvmbasics.R;
import com.journaldev.androidmvvmbasics.databinding.ActivityMainBinding;
import com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.setViewModel(new LoginViewModel());
activityMainBinding.executePendingBindings();
}
@BindingAdapter({"toastMessage"})
public static void runMe(View view, String message) {
if (message != null)
Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
}
}
Gracias a DataBinding, la clase ActivityMainBinding
se genera automáticamente a partir del diseño. El método @BindingAdapter
se activa cada vez que el atributo toastMessage definido en el Button cambia. Debe usar el mismo atributo que se define en el XML y en el ViewModel. Por lo tanto, en la aplicación anterior, el ViewModel actualiza el Modelo escuchando los cambios en la Vista. Además, el Modelo puede actualizar la vista a través del ViewModel usando notifyPropertyChanged
. La salida de la aplicación anterior en acción se muestra a continuación: Esto pone fin a este tutorial sobre Android MVVM usando DataBinding. Puedes descargar el proyecto desde el enlace proporcionado a continuación.
Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern