ViewModels y LiveData: Patrones + AntiPatterns

El sitio web oficial ha dado algunas explicaciones sobre ViewModel, como la imposibilidad de introducir el Contexto de actividad en ViewModel, pero todavía hay muchas precauciones o expresiones idiomáticas (sintaxis común) para usar ViewModel mejor.

Este artículo hace referencia a una publicación de blog recomendada por el sitio web oficial de Google: ViewModels y LiveData: Patterns + AntiPatterns

Echemos un vistazo a la descripción general del uso de componentes arquitectónicos en el sitio web oficial:

Idealmente, ViewModels no debería introducir nada en Android. Esto mejora la capacidad de prueba, la seguridad de fugas y la modularidad. La regla general es asegurarse de que no haya Android. * Importar (excepto android.arch. *) En ViewModel. Lo mismo se aplica a la capa de presentador.

1Nota 1: No introduzca la clase de marco de Android en ViewModel (o capa presentadora)  

Las declaraciones condicionales, los bucles y las decisiones generales deben hacerse en ViewModel u otras capas de la aplicación, no en Activity o Fragment. La vista generalmente no se prueba en la unidad (a menos que use Robolectric), por lo que cuantas menos líneas de código, mejor. La vista VIew solo debe saber cómo mostrar datos y enviar eventos de usuario al ViewModel (o Presentador). Esto se conoce como pasivo  Ver modo de visualización pasiva.

Es decir: el código lógico minimizado debe conservarse en Actividad y fragmento.

2Nota 2: En el artículo "Resumen de aprendizaje del sitio web oficial de ViewModel" , se ha explicado que debido a que el ciclo de vida de ViewModel es mayor que la Actividad, no es posible introducir el contexto de Actividad o fragmento en la capa de ViewModel para evitar pérdidas de memoria o fallas.

✅: La interacción entre ViewModels y View, la forma recomendada es el modo de observador, como el uso de LiveData u observadores en otras bibliotecas. Es decir, no pase datos directamente a la capa de interfaz de usuario, pero deje que la capa de la interfaz de usuario observe los cambios de datos, es decir, los datos controlan la interfaz de usuario.

   

 

 Evite los ViewModels hinchados

: :Carga de datos conscientes del ciclo de vida con componentes de arquitectura

✅: Eso es para evitar poner la lógica de procesamiento de datos directamente en ViewModel, pero en LiveData (u otro observable), porque el propósito del diseño de ViewModels es crear y organizar LiveData. Parte de la lógica se puede transferir a la capa de presentador.

✅: Encapsula la lógica en LiveData, también es beneficioso reutilizar el mismo LiveData en muchos ViewModels, combinar múltiples fuentes de LiveData a través de MediatorLiveData o usarlas en un Servicio

Ver el siguiente código:

public class JsonViewModel extends AndroidViewModel {
  // You probably have something more complicated
  // than just a String. Roll with me
  private final MutableLiveData<List<String>> data =
      new MutableLiveData<List<String>>();
  public JsonViewModel(Application application) {
    super(application);
    loadData();
  }
  public LiveData<List<String>> getData() {
    return data;
  }
  private void loadData() {
    new AsyncTask<Void,Void,List<String>>() {
      @Override
      protected List<String> doInBackground(Void... voids) {
        File jsonFile = new File(getApplication().getFilesDir(),
            "downloaded.json");
        List<String> data = new ArrayList<>();
        // Parse the JSON using the library of your choice
        return data;
      }
      @Override
      protected void onPostExecute(List<String> data) {
        this.data.setValue(data);
      }
    }.execute();
  }
}

El código anterior pone mucha lógica de procesamiento o adquisición de datos en la capa ViewModel. Esto no es razonable y debe cambiarse por el siguiente método:

public class JsonViewModel extends AndroidViewModel {
  private final JsonLiveData data;
  public JsonViewModel(Application application) {
    super(application);
    data = new JsonLiveData(application);
  }
  public LiveData<List<String>> getData() {
    return data;
  }
}
public class JsonLiveData extends LiveData<List<String>> {
  private final Context context;
  public JsonLiveData(Context context) {
    this.context = context;
    loadData();
  }
  private void loadData() {
    new AsyncTask<Void,Void,List<String>>() {
      @Override
      protected List<String> doInBackground(Void… voids) {
        File jsonFile = new File(getApplication().getFilesDir(),
            "downloaded.json");
        List<String> data = new ArrayList<>();
        // Parse the JSON using the library of your choice
        return data;
      }
      @Override
      protected void onPostExecute(List<String> data) {
        setValue(data);
      }
    }.execute();
  }
}

Ahora nuestro ViewModel se ha vuelto bastante simple. Nuestro LiveData ahora encapsula completamente el proceso de carga y solo carga los datos una vez.

 

Usa la capa de repositorio de datos

De acuerdo con la descripción en el blog de referencia, de hecho, la documentación oficial del sitio web también introduce que cuando los datos pueden provenir de la red, memoria local o caché, etc., debe agregar una capa de repositorio para encapsular estas operaciones de procesamiento de datos, y luego la capa de presentación (presentador o ViewModels ) No es necesario preocuparse por el origen de los datos. La capa de repositorio es la entrada unificada a los datos.

✅: Es decir: agregue una capa de depósito de datos como la única entrada a datos externos.

 

Procesando estado de datos

Considere el siguiente escenario: está observando LiveData expuesto por un ViewModel que contiene un conjunto de datos de lista para mostrar. ¿Cómo distingue la vista entre datos cargados, errores de red y listas vacías?
Puede exponer LiveData <MyDataState> desde ViewModel. Por ejemplo, MyDataState puede contener información sobre si los datos se están cargando actualmente, si se han cargado o fallado correctamente, o alguna otra información de datos sin procesar.

✅: Es decir: use una clase contenedora para exponer la información de estado de sus datos, o use otro LiveData.

 

Guardar el estado de la actividad

El estado de la actividad es la información necesaria para recrear la pantalla después de que la actividad desaparece, lo que indica que la actividad se ha destruido o el proceso se ha terminado. La rotación es la situación más obvia, la hemos cubierto con ViewModels. Si el estado se guarda en ViewModel, el estado es seguro.
Sin embargo, es posible que necesite restaurar el estado en otros escenarios donde ViewModel también ha desaparecido: por ejemplo, cuando los recursos del sistema operativo son insuficientes y hacen que su proceso finalice.
Para guardar y restaurar efectivamente el estado de la IU, use una combinación de persistencia, onSaveInstanceState () y ViewModels.
Para ver ejemplos, consulte: ViewModels: Persistence, onSaveInstanceState (), restablezca el estado y el cargador de la IU

 

El evento

El evento al que se hace referencia aquí solo ocurre una vez. ViewModels expone datos, pero ¿qué pasa con los eventos? Por ejemplo, los eventos de navegación, las aplicaciones de permisos o la visualización de mensajes de Snackbar son acciones que solo deben realizarse una vez.
El concepto del evento no coincide exactamente con la forma en que LiveData almacena y restaura datos. Considere un ViewModel con los siguientes campos:

LiveData<String> snackbarMessage = new MutableLiveData<>();

La actividad comienza a observar esta operación, y ViewModel completa la operación, por lo que el mensaje debe actualizarse:

snackbarMessage.setValue("Item saved!");

En este momento, la Actividad obtiene el cambio de valor y muestra la Snackbar. Parece que no hay problema, pero si se gira la pantalla, se crea una nueva actividad y se recibirá el valor anterior, lo que hará que Snackbar muestre el mensaje nuevamente.

Para resolver este problema, no debe usar bibliotecas de terceros ni extender los componentes arquitectónicos, sino que debe tratar los eventos como parte del estado.

Es decir: diseñar el evento como parte del estado. Para obtener más información, consulte: LiveData con SnackBar, Navigation y otros eventos (el caso SingleLiveEvent) .

 

Presta atención a la fuga de ViewModel

Considere el siguiente escenario:

La capa de presentación usa el modo de observador, y la capa de datos usa devoluciones de llamada de interfaz.

Si el usuario sale de la aplicación, la Vista desaparece y ya no se observa ViewModel. Si la capa del repositorio es un singleton o el mismo que el ciclo de vida de la aplicación, el repositorio no se destruirá hasta que se finalice el proceso. El proceso solo finalizará cuando los recursos del sistema sean escasos o el usuario lo mate manualmente. Si hay una devolución de llamada ViewModel en la capa del repositorio, ViewModel perderá temporalmente la memoria.

 

Si ViewModel es relativamente liviano o la operación puede finalizar rápidamente, no habrá un impacto significativo. Pero este no es siempre el caso. Idealmente, mientras no haya una Vista observando el Modelo de Vista, entonces el Modelo de Vista debería ser destruido.

Hay muchas formas de hacer esto:

  • A través del método onCleared () de ViewModel, puede indicarle a la capa del repositorio que suelte la devolución de llamada en ViewModel.
  • En el repositorio, puede usar referencias débiles o puede usar EventBus (ambos son mal utilizados o incluso considerados dañinos)
  • Como usar LiveData para interactuar entre View y ViewModel, usar LiveData para interactuar entre ViewModel y la capa de repositorio

✅: Es necesario tener en cuenta el impacto de las condiciones de contorno, las fugas y las operaciones que consumen mucho tiempo en los objetos de instancia en la arquitectura.

 

Use LiveData en la capa del repositorio

Para evitar filtraciones de ViewModel y callback hell, la capa de repositorio se puede observar así:

Cuando se borra ViewModel, o finaliza el ciclo de vida de la vista, se borra la relación de suscripción entre ViewModel y el repositorio.

Si prueba este método, hay un problema: si no tiene acceso a LifecycleOwner, ¿cómo se suscribe al repositorio desde ViewModel? Usar transformaciones es una forma muy conveniente de resolver este problema. Transformations.switchMap le permite crear un nuevo LiveData en respuesta a cambios en otras instancias de LiveData. También permite transportar información del ciclo de vida del observador a lo largo de la cadena:

LiveData<Repo> repo = Transformations.switchMap(repoIdLiveData, repoId -> {
        if (repoId.isEmpty()) {
            return AbsentLiveData.create();
        }
        return repository.loadRepo(repoId);
    }
);

En este ejemplo, cuando el activador obtiene una actualización, la función se aplica y los resultados se programan en sentido descendente. La actividad observará el repositorio y se usará el mismo LifecycleOwner para la llamada repository.loadRepo (id).

✅  Es decir: cuando necesita obtener un objeto Lifecycle en ViewModel , puede usar Transformaciones en este momento

 

Expandir LiveData

La forma habitual de usar LiveData es usar MutableLiveData en ViewModel y exponer un método get.

Si necesita más funciones, LiveData extendido le notificará cuando haya observadores activos. Por ejemplo, esto es útil cuando desea comenzar a escuchar la ubicación o los servicios del sensor:

public class MyLiveData extends LiveData<MyData> {

    public MyLiveData(Context context) {
        // Initialize service
    }

    @Override
    protected void onActive() {
        // Start listening
    }

    @Override
    protected void onInactive() {
        // Stop listening
    }
}

¿Cuándo no se debe extender LiveData?

También puede usar onActive () para iniciar algunos servicios que cargan datos, pero a menos que tenga una buena razón, no necesita esperar a LiveData hasta que pueda ser observado. Algunos patrones comunes:

❌   Usualmente no extiendes LiveData. Deje que su actividad o fragmento le indique al ViewModel cuándo comenzar a cargar datos

 

Por último:

Con respecto al uso de ViewModel, el sitio web oficial recomienda un blog: ViewModels: un ejemplo simple

Un ejemplo de uso de ViewModel en Android codelab: https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

Una introducción oficial al uso de ViewModel en YouTube: https://www.youtube.com/watch?v=c9-057jC1ZA

Android Jetpack: ViewModel | Video de enseñanza del chino: https://mp.weixin.qq.com/s/uGWH1os8Kq3Pp6_x5hXI8Q

Para el uso de componentes de  arquitectura , el sitio web oficial ofrece algunos ejemplos simples:

https://github.com/googlesamples/android-architecture-components/blob/master/README.md

Y un ejemplo de uso de Jetpack recomendado por el sitio web oficial:   Sunflower

 

Una muestra del navegador Github del cliente GitHub con componentes de arquitectura de Android utilizando la versión MVVM de los componentes del ciclo de vida 

Esta es una aplicación de muestra que utiliza componentes de arquitectura de Android con Dagger 2.

NOTA:  Es un ejemplo relativamente más complejo y completo, por lo que si no está familiarizado con los  Componentes de Arquitectura , le recomendamos que consulte primero otros ejemplos en este repositorio.

El sitio web oficial presenta que este es un ejemplo relativamente más complicado y completo. Es necesario tener una cierta comprensión del marco de inyección de dependencia Dagger2. Es muy muy recomendable aprender este ejemplo.

 

Después, tendré tiempo para escribir más código y resumir la experiencia de aprendizaje de algunos componentes arquitectónicos.

 

82 artículos originales publicados · Me gusta 86 · Visita 110,000+

Supongo que te gusta

Origin blog.csdn.net/unicorn97/article/details/82151169
Recomendado
Clasificación