Scoped Subcomponents y Singletons – Dagger 2

Imagen de cabecera del artículo. Scoped subcomponents

Para optimizar el uso de la memoria, el inyector de dependencias Dagger 2 permite crear subcomponentes con ámbito (scoped subcomponents) cuya finalidad es la de utilizar instancias únicas con ámbito (scoped singletons), es decir, instancias que solo permanecerán en la memoria el tiempo que el subcomponente esté vivo y no todo el tiempo, al contrario que si fueran inicializadas desde el componente, el cual suele estar vivo durante toda la aplicación.

Ámbitos (Scopes)

Todos los componentes tienen un ámbito. El ámbito viene determinado por cuándo creamos y cuándo eliminamos la instancia del componente. Normalmente, el componente se instancia en la clase aplicación, por lo que este tiene ámbito de aplicación. Si creáramos y destruyésemos la instancia del componente en cada activity, sería de ámbito activity. Es decir, que el ámbito es la clase donde se crea y se destruye.

Normalmente nos interesará que el componente de un ámbito inferior que el de aplicación permita inyectar todo lo que puede inyectar un componente de ámbito de aplicación y además otros objetos más específicos para su propio ámbito. Esto se consigue con los subcomponentes. Un componente de ámbito superior puede contener subcomponentes de ámbitos inferiores que permiten inyectar todo lo que se declare en sus módulos además de todo lo que pueda inyectar el componente padre.

Para ilustrarlo mejor, vamos a suponer el siguiente caso: tenemos la clase ClaseA, la cual queremos inyectar en varios lugares de la aplicación, y también tenemos la clase ClaseB, la cual solo inyectaremos en las activities. Crearemos un componente y un módulo que contenga un método provider para la clase ClaseA y un subcomponente y un módulo que tenga un método provider para la clase ClaseB.

Componente y subcomponente

Un subcomponente es igualmente un componente pero se crea con la anotación @Subcomponent:

Un subcomponente puede ser obtenido mediante el componente padre, para lo cual debemos crear en este un método que lo devuelva:

El nombre del método que devuelve el subcomponente no importa; se ha usado plus() porque parece que se viene haciendo desde Dagger 1. Lo importante es que devuelva un objeto del tipo del subcomponente y que reciba los módulos que lo conforman.

Observa que hemos movido el método inject(MainActivity) del componente al subcomponente, ya que para las activities queremos utilizar el subcomponente de ámbito activityy no el componente raíz. Si hubiera más activities, crearíamos también los métodos necesarios para inicializar la inyección en estas.

Módulos

El módulo de Componente contendrá un método provider para inyectar instancias de ClaseA y el módulo de Subcomponente tendrá uno para inyectar instancias de ClaseB:

Instanciación

Como hemos dicho, lo que verdaderamente determina el ámbito del subcomponente es dónde creas y dónde destruyes su instancia. En este caso, como queremos que el ámbito sea de activity, debemos crear y destruir el subcomponente cada vez que se cree y se destruya una activity para que cada una tenga el suyo.
Pero para acceder al subcomponente necesitamos la instancia del componente padre:

Tras llamar a inject() de Subcomponente se inicializarán tanto claseB como claseA. Esto es así porque, como ya hemos dicho al principio, un componente de un ámbito inferior puede inyectar todo aquello que puede inyectar su componente padre.

No habríamos podido inicializar la inyección mediante el componente padre utilizando MiAplicacion.getComponente().inject(this) porque:

  1. El método inject() está en el subcomponente.
  2. Aunque lo moviéramos al componente, el componente no tiene acceso a los providers del módulo del subcomponente, por lo que no podría inyectar claseB

En definitiva, el subcomponente tiene ámbito de activity porque solo inicializa la inyección en activities y porque se instancia en el onCreate() y, al ser un campo de la activity, será destruido cuando esta se destruya. Pasa lo mismo con el ámbito de mComponente: su ámbito es de aplicación porque es creado en la clase MiAplicación y será destruido cuando la aplicación se destruya.

Singletons con ámbito

Un componente tiene un solo ámbito. El ámbito @Singleton es el más grande y está reservado para el componente raíz. Como normalmente lo creamos en la clase de la aplicación, el ámbito de @Singleton es de aplicación y todas las instancias generadas por un método provider marcado con @Singleton son únicas en toda la aplicación. Si queremos generar instancias únicas también en los subcomponentes, tenemos que crear otra anotación diferente.
Fíjate en la declaración de la anotación @Singleton:

Y no hay nada más. El uso de la anotación @Scope es lo que le dice a Dagger que las instancias @Singleton pertenecen a un ámbito. Así que, para diferenciar entre el ámbito del componente y el del subcomponente, tenemos que crear una anotación nueva marcada también con @Scope:

El nombre que le pongamos a la anotación da igual, ya que, como hemos visto antes, el ámbito lo define el dónde instanciamos el subcomponente y el dónde los destruimos. Lo importante es que el subcomponente no tenga la misma anotación que el componente padre para que se reconozcan como ámbitos distintos.

Nota: padre e hijo no pueden utilizar la misma anotación, pero si pueden hacerlo componentes hermanos, es decir, si el padre tiene dos subcomponentes hijo, ambos pueden utilizar la misma anotación que acabamos de crear.

Ahora, supongamos que queremos convertir las instancias tanto de ClaseA y ClaseB en instancias únicas. Lo único que tenemos que hacer es marcar cada componente y cada método provider con la anotación pertinente. Como ya hemos dicho, @Singleton está reservada para el componente raíz:

Y después marcar los métodos provider que queramos con la misma anotación que su componente:

ModuloComponente.class

ModuloSubcomponente.class

Y a partir de ahora, las instancias de ClaseA y ClaseB serán únicas. ClaseA será única en toda la aplicación, pero ClaseB será única solo dentro de una misma activity, ya que, al iniciar una activity nueva, se creará un nuevo subcomponente que generará una instancia única de ClaseB distinta.

Atento, no te líes: una instancia será única siempre que la obtengas de la misma instancia de un componente o subcomponente. Por el contrario, dos instancias de un mismo componente o subcomponente generarán dos instancias únicas de la clase inyectada, pero distintas entre sí. Es decir:

Si llamamos al método inject() de subcomponente1 se obtendría siempre la misma instancia, y si llamamos al inject() de subcomponente2 se obtendría siempre la misma instancia, pero sería distinta de la obtenida con subcomponente1. Las instancias son únicas por componente.

Y lo mismo sucederá si el componente es reinstanciado: las instancias únicas que genere el nuevo serán distintas de las instancias únicas del anterior.

0 Comentarios

Contesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

©2017 Codictados Comunidad libre para el aprendizaje de codigo Online

o

Inicia Sesión con tu Usuario y Contraseña

o    

¿Olvidó sus datos?