Dagger 2 para principiantes de Android - Parte avanzada I

Esta historia es la sexta parte de la serie, Dagger 2 para principiantes de Android. Si no leyó el anterior, puede comenzar desde abajo.

Serie Pitstops

  • Dagger 2 para principiantes de Android - Introducción
  • Dagger 2 para principiantes de Android - DI parte I
  • Daga 2 para principiantes de Android - DI parte II
  • Dagger 2 para principiantes de Android - Dagger 2 parte I
  • Daga 2 para principiantes de Android - Daga 2 parte II
  • Dagger 2 para principiantes de Android - Dagger 2 Advanced parte I (estás aquí)
  • Dagger 2 para principiantes de Android - Dagger 2 Advanced parte II

Anteriormente en Dagger 2 ..

Analizamos la anatomía de la clase generada por la daga y vimos cómo la daga utiliza el patrón de construcción para entregarnos las dependencias requeridas.

También jugamos y vimos un ejemplo básico del uso de @module y @provides anotación.

Prefacio

Esta publicación puede ser un poco larga. Por lo general, mantengo mis publicaciones sin extender 800 palabras, solo para hacer justicia a la capacidad de comprensión de mi lector. Quería dividirme en partes, pero la razón por la que esta publicación es como un maratón es que, si tienes un largo descanso en el medio, mientras resuelves un problema de dependencia difícil, hay algunas posibilidades de que te pierdas.

Pero en el lado más seguro, he incluido puntos de control en esta publicación. Busque una línea de 3 puntos o la palabra (Punto de control) entre paréntesis. Ahí es donde tomas un pequeño descanso y permites que tu mente procese y absorba, esto es para los principiantes que están aprendiendo Dagger 2 y DI.

¿Por qué te estoy diciendo todo esto? bueno, también soy un tipo de experiencia de usuario: valoro la empatía y la experiencia del usuario en todo :-P
¡Feliz lectura!

Android de la casa

Créditos de imagen: http://androidcentral.com/

Hasta ahora, estábamos jugando con un proyecto Java normal. Espero que la mayoría de ustedes ahora tengan una idea sobre la inyección de dependencia y cómo Dagger 2 nos ayuda a lograrlo. Vamos a sumergirnos en un escenario de Android en tiempo real e intentar construir nuestro proyecto usando Dagger 2.

Para que todos estén en la misma página, como los laboratorios de códigos de Google, he creado un pequeño proyecto kickstarter. Nuestro objetivo será eliminar las dependencias difíciles en este proyecto.

Proyecto Kickstarter

Inspiración del concepto

Este concepto de explicación fue inspirado por una de las charlas de GDD de mi buen amigo Chintan Soni. Me gustaría agradecerle por permitirme usar su concepto y algunos de sus trabajos en este proyecto.

Descripción del Proyecto

Este es un proyecto muy simple: busca usuarios aleatorios utilizando la API de usuarios aleatorios y lo muestra en un RecyclerView. No pasaré mucho tiempo explicando el proyecto, puede ser solo un resumen. Pero lea el código para una comprensión más profunda, para que nuestras integraciones de Dagger 2 sean más fáciles.

#Clases y paquetes

  • MainActivity.java: solicita la API y muestra los elementos en RecyclerView
  • Paquete de modelo: POJO para la respuesta API, creado utilizando el esquema JSON para POJO
  • RandomUsersAdapter.java - Adaptador para RecyclerView

Dependencias involucradas

Para mostrar usuarios aleatorios en un RecyclerView a través de una llamada API, están implicadas las siguientes dependencias y bibliotecas.

  • Retrofit - para hacer llamadas API
  • GsonBuilder & Gson - para análisis y manipulación JSON
  • HttpLoggingInterceptor: para registrar operaciones de red
  • OkHttpClient - cliente para Retrofit
  • Picasso - manejo de imágenes (en Adaptador)

Como vimos en nuestros ejemplos anteriores, en nuestra actividad principal, tenemos las siguientes dependencias. Y cada vez que se llama a MainActivity, estas instancias se crean una y otra vez.

(Control)

Problemas a resolver

Si observa nuestra actividad principal, puede notar los siguientes problemas

# Inicializaciones torpes

Cuando observa nuestro método onCreate de MainActivity, puede encontrar todas las inicializaciones y se ve muy torpe. No podemos seguir agregando más inicializaciones sobre la marcha. Necesitamos una forma adecuada de hacerlo.

# Estabilidad

También tenemos que encontrar una manera de probar nuestro código. Incluso Picasso en nuestro adaptador rompe la regla de comprobabilidad. Sí, sería genial inyectar el objeto Picasso a través del constructor, también sería bueno.

Hagámoslo más complicado.

Las dependencias que mencioné anteriormente en nuestra MainActivity fueron solo para que se sienta cómodo con el proyecto kickstarter. Si profundizamos, puede haber dependencias adicionales que pueden estar presentes en nuestra actividad principal, en un proyecto típico en tiempo real. Entonces, agreguemos dependencias adicionales a nuestra actividad principal.

Consulte la siguiente rama para agregar dependencias adicionales

Además de las dependencias anteriores, las adicionales son

  • Dependencia de archivo: para mantener el caché
  • Caché - para caché de red
  • OkHttp3Downloader: un descargador que usa OkHttpClient para descargar imágenes
  • Picasso - para manejar imágenes de red

Ahora, las dependencias completas se verán así

(Control)

El secreto

Muchos de nosotros (incluido yo) encontramos DI y Dagger 2 poco difícil porque faltaba algo. Faltaba una pequeña pieza para conectar los nodos. El eslabón perdido o el secreto no es más que el gráfico de dependencia

Gráfico de dependencia

El gráfico de dependencia no es más que un diagrama, que explica las dependencias de las clases a través de marcas de flecha o líneas. Garabatear un gráfico D para los proyectos realmente hará que nuestra implementación sea mucho más fácil (se dará cuenta al final). Aquí está el gráfico DI para nuestro proyecto.

Gráfico de dependencia

Los cuadros de color verde significan que son de primer orden en dependencias, es decir, que nadie los quiere. Solo necesitan las siguientes dependencias.

¿Cómo leer / atravesar el diagrama gráfico? Es como si Picasso tuviera 2 dependencias: OkHttp3Downloader y Context.

Para obtener usuarios aleatorios de la API, necesita Retrofit. Eso, a su vez, requiere 2 dependencias: GsonConvertFactory y OkHttpClient. Ellos, a su vez, necesitan sus propias dependencias, etc.

Tómese su tiempo, vea el código en MainActivity y el diagrama uno al lado del otro para comprender mejor

(Control)

Manejo de la inyección de dependencia con Dagger 2

Consulte la siguiente rama, encontrará la implementación de Dagger 2

Nota :

  • RandomUsersAPI y RandomUsersApi son iguales. Solo el error tipográfico al crear el diagrama.
  • RandomUsersComponent será diferente cuando compares la rama anterior y la esencia que uso desde aquí. Te sugiero que tomes la siguiente rama y te guíes con las esencias. Mantenga la rama anterior como referencia.
  • No olvides destacar el proyecto si realmente te ha ayudado a aprender

Paso 1: Configurar Daga

Para configurar Dagger 2, consulte build.gradle de la aplicación en la rama anterior o simplemente agregue las siguientes líneas en su archivo build.gradle.

Paso 2: Crear componente

Un componente actuará como una interfaz pública para todo su gráfico de dependencia. La mejor práctica de usar un componente es exponer solo la dependencia de nivel superior y mantener otra dependencia interna bajo el capó.

Eso significa que he resaltado las clases en color verde en nuestro gráfico de dependencia. Son las dependencias de nivel superior: RandomUsersAPI y Picasso. Expongamos solo a ellos.

Cree un componente llamado RandomUserComponent y exponga las siguientes clases RandomUsersApi y Picasso.

Ahora, ¿cómo sabrá el componente de dónde obtener las dependencias RandomUsersApi y Picasso? Ahí es donde entran los módulos.

Paso 3: creación de módulos

Los módulos proporcionarán dependencias bajo el capó a las dependencias más externas: RandomUsersApi y Picasso.

Ahora necesitamos mover el código de MainActivity a diferentes módulos. Al observar el D-Graph y el componente, podemos decidir qué módulos necesitamos.

Primero es RandomUsersModule, que proporciona RandomUsersApi, GsonConverterFactory, Gson y Retrofit

En segundo lugar, PicassoModule, que proporciona Picasso y OkHttp3Downloader.

Para la actualización en nuestro RandomUsersModule, necesitamos OkHttpClient. Lo que a su vez necesita pocas dependencias. Entonces, ¿por qué no crear un módulo separado para él?

Vamos a crear OkHttpClientModule, que proporciona OkHttpClient, Cache, HttpLoggingInterceptor y File

Nuestros módulos de nivel superior están listos. Pero PicassoModule y OkHttpClientModule necesitan Context y también enfrentaremos otras situaciones donde necesitamos Context. Entonces, ¿por qué no un módulo para ello?

Paso 3: conectar todos los módulos

Ahora, tenemos todos los módulos y componentes en su lugar, como la imagen que se muestra a continuación. Pero, ¿cómo pasamos el contexto a otros módulos? Necesitamos vincular los módulos que dependen de llegar a otros.

Aquí es donde viene incluido el atributo. incluye atributo incluye otra dependencia del módulo involucrada en el módulo actual.

¿Qué deben incluirse todos los módulos?

  • RandomUsersModule necesita OkHttpClientModule
  • OkHttpClientModule necesita ContextModule
  • PicassoModule necesita OkHttpClientModule y ContextModule. Pero como OkHttpClientModule ya está vinculado con ContextModule, incluyamos solo OkHttpClientModule

Al proporcionar lo anterior, hemos vinculado todos los módulos.

Vinculado todos los módulos :-)

Paso 4: Componente educativo

Ahora, todos nuestros módulos están conectados y pueden comunicarse entre sí. Ahora solo necesitamos decirle o educar a los componentes de qué módulos deben depender para proporcionar las dependencias.

Al igual que decirle a los módulos acerca de sus dependencias con el atributo incluye, necesitamos decirle al componente acerca de sus dependencias con el atributo de módulos.

Teniendo en cuenta las necesidades de los componentes (métodos: getRandomUserService () y getPicasso ()), incluyamos el módulo RandomUsersModule y PicassoModule en nuestro componente usando los atributos de los módulos.

Los componentes y los módulos están conectados :-)

Paso 5: compilarlo

Es hora de construir el proyecto. Si ha realizado todos los pasos anteriores correctamente, Dagger nos habría creado RandomUserComponent utilizando un patrón de construcción que ahora puede proporcionar dependencias según nuestra solicitud.

Ahora, si miramos nuestra actividad principal, podemos adquirir fácilmente Picasso y RandomUsersApi con la ayuda de RandomUserComponent.

Consulte la rama, puede haber alguna modificación en el constructor del adaptador para pasar el objeto Picasso.

Paso 6: ¡felicítate!

¡Sí! lo hiciste. Usaste Dagger 2 en una aplicación de Android. Felicítese y tome un descanso (punto de control). Siéntase libre de jugar con la clase generada también.

¡Pero hay un problema!

¿Qué? ¿qué problema?

Cada vez que llama a .build (), crea nuevas instancias de todos los objetos o dependencias que le ha indicado que le proporcione. Entonces, en ese caso, ¿por qué Dagger no sabe que necesito una sola instancia para Picasso? En otras palabras, ¿cómo le decimos a Dagger que nos proporcione dependencia singleton (instancia única)?

Ahí es donde entra la anotación @Scope para nuestro rescate.

Anotación @Scope

La anotación @Scope le dice a Dagger que cree una sola instancia, incluso si se llama a .build () muchas veces. Hará que la dependencia funcione como singleton.

Necesitamos crear una nueva interfaz para crear nuestro alcance personalizado.

@Retención: es la política de retención de la anotación. Aquí hemos dado instrucciones para retener la anotación hasta la clase. Lea más sobre retención aquí.

Uso de alcance personalizado

Para usar nuestro alcance personalizado, necesitamos comenzar en el nivel de componente y luego poner todos los métodos que necesitamos como un singleton.

¡Así es como creamos la instancia única!

Ahora, un problema más!

Por lo general, para cada aplicación usaremos dos tipos de contexto. Contexto de aplicación y contexto de actividad. ¿Cómo lo proporcionamos? para ApplicationContext, podemos usar nuestro ContextModule para proporcionar. Entonces, creemos otro módulo llamado ActivtiyModule para proporcionar contexto de Actividad.

Pero esto no resuelve nuestro problema. Como nuestra Daga se confundirá sobre qué contexto usar. Como tiene 2 módulos, cada uno de los cuales proporciona Contexto, arrojará un error.

¿Cómo podemos decirle a Dagger que use ApplicationContext para esta dependencia y el contexto de Actividad para esta dependencia? Las anotaciones de @Named harán ese trabajo por usted.

Anotaciones @Named

Esta anotación nos ayuda a diferenciar el contexto. Veamos cómo usarlos. Podemos diferenciar el método context () en ActivityModule y ContextModule agregando la anotación @Named como se muestra a continuación.

Luego, le decimos a Dagger que use el contexto respectivo, como a continuación

Alternativa a la anotación @Named - @Qualifier

La alternativa a la anotación @Named es la anotación @Qualifier. Para crear ApplicationContext usando @Qualifier, necesitamos crear una @interface separada y usarla siempre que sea necesario. Primero creemos un @interface ApplicationContext.

Luego asignamos esta interfaz al método del proveedor de dependencia: context ()

Luego le decimos a todos los parámetros para usar este contexto.

Consulte la siguiente confirmación para el uso de anotaciones @Qualifier.

TL; DR

Hasta ahora, tomamos un proyecto de ejemplo de kickstarter e intentamos desacoplar e inyectar las dependencias utilizando Dagger 2 API y anotaciones.

También vimos 3 nuevas anotaciones. Uno es @Scope, que se utiliza para obtener dependencias singleton. El siguiente es @Named, que se utiliza para diferenciar a los proveedores de dependencias. Otra es la anotación @Qualifier, una alternativa a la anotación @Named y vimos la implementación de ambas.

¿Que sigue?

Hasta ahora solo vimos dependencias de nivel de aplicación. A continuación, veremos las dependencias del nivel de actividad y también crearemos múltiples componentes e interactuaremos también con ellos.

Además, mira mis otras historias.

No te olvides de golpear a la estrella en el Proyecto GitHub.

Gracias por usar su valioso tiempo para leer este artículo. ¿Le gustó? Aplauda su para decir "¡gracias!" Y ayude a otros a encontrar este artículo.