Patrón de diseño MVVM de Android

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.

AndroidMVVMBasics

Enlace del Proyecto en Github

Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern