He estado trabajando con dagger2 por un tiempo. Y me confundí si debía crear un componente / módulo propio para cada Actividad / Fragmento. Ayúdame a aclarar esto:
Por ejemplo, tenemos una aplicación y la aplicación tiene alrededor de 50 pantallas. Implementaremos el código siguiendo el patrón MVP y Dagger2 para DI. Supongamos que tenemos 50 actividades y 50 presentadores.
En mi opinión, normalmente deberíamos organizar el código así:
Cree un AppComponent y AppModule que proporcionará todos los objetos que se utilizarán mientras la aplicación esté abierta.
@Module public class AppModule { private final MyApplicationClass application; public AppModule(MyApplicationClass application) { this.application = application; } @Provides @Singleton Context provideApplicationContext() { return this.application; } //... and many other providers } @Singleton @Component( modules = { AppModule.class } ) public interface AppComponent { Context getAppContext(); Activity1Component plus(Activity1Module module); Activity2Component plus(Activity2Module module); //... plus 48 methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....) }
Crear ActivityScope:
@Scope @Documented @Retention(value=RUNTIME) public @interface ActivityScope { }
Cree un componente y un módulo para cada actividad. Normalmente los pondré como clases estáticas dentro de la clase Activity:
@Module public class Activity1Module { public LoginModule() { } @Provides @ActivityScope Activity1Presenter provideActivity1Presenter(Context context, /*...some other params*/){ return new Activity1PresenterImpl(context, /*...some other params*/); } } @ActivityScope @Subcomponent( modules = { Activity1Module.class } ) public interface Activity1Component { void inject(Activity1 activity); // inject Presenter to the Activity } // .... Same with 49 remaining modules and components.
Esos son solo ejemplos muy simples para mostrar cómo implementaría esto.
Pero un amigo mío me acaba de dar otra implementación:
Cree PresenterModule que proporcionará a todos los presentadores:
@Module public class AppPresenterModule { @Provides Activity1Presenter provideActivity1Presentor(Context context, /*...some other params*/){ return new Activity1PresenterImpl(context, /*...some other params*/); } @Provides Activity2Presenter provideActivity2Presentor(Context context, /*...some other params*/){ return new Activity2PresenterImpl(context, /*...some other params*/); } //... same with 48 other presenters. }
Cree AppModule y AppComponent:
@Module public class AppModule { private final MyApplicationClass application; public AppModule(MyApplicationClass application) { this.application = application; } @Provides @Singleton Context provideApplicationContext() { return this.application; } //... and many other provides } @Singleton @Component( modules = { AppModule.class, AppPresenterModule.class } ) public interface AppComponent { Context getAppContext(); public void inject(Activity1 activity); public void inject(Activity2 activity); //... and 48 other methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....) }
Su explicación es: no tiene que crear componentes y módulos para cada actividad. Creo que la idea de mi amigo no es buena en absoluto, pero corríjame si me equivoco. Estas son las razones:
Muchas pérdidas de memoria :
- La aplicación creará 50 presentadores incluso si el usuario solo tiene 2 actividades abiertas.
- Una vez que el usuario cierra una actividad, su presentador seguirá siendo
¿Qué sucede si quiero crear dos instancias de una actividad? (cómo puede crear dos presentadores)
La aplicación tardará mucho en inicializarse (porque tiene que crear muchos presentadores, objetos, ...)
Perdón por una publicación larga, pero por favor ayúdame a aclarar esto para mí y para mi amigo, no puedo convencerlo. Tus comentarios serán muy apreciados.
/ ------------------------------------------------- ---------------------- /
Edite después de hacer una demostración.
Primero, gracias por la respuesta de @pandawarrior. Debería haber creado una demostración antes de hacer esta pregunta. Espero que mi conclusión aquí pueda ayudar a alguien más.
- Lo que ha hecho mi amigo no causa pérdidas de memoria a menos que ponga algún Scope en los métodos Provides. (Por ejemplo @Singleton o @UserScope, ...)
- Podemos crear muchos presentadores, si el método Provides no tiene ningún alcance. (Entonces, mi segundo punto también está mal)
- Dagger creará los presentadores solo cuando sean necesarios. (Entonces, la aplicación no tardará mucho en inicializarse, estaba confundido por Lazy Injection)
Entonces, todas las razones que he dicho anteriormente son en su mayoría incorrectas. Pero eso no significa que debamos seguir la idea de mi amigo, por dos razones:
No es bueno para la arquitectura de la fuente, cuando inicia a todos los presentadores en módulo / componente. (Viola el principio de segregación de interfaz , quizás también el principio de responsabilidad única).
Cuando creamos un componente de alcance, sabremos cuándo se crea y cuándo se destruye, lo cual es un gran beneficio para evitar pérdidas de memoria. Entonces, para cada actividad deberíamos crear un componente con un @ActivityScope. Imaginemos, con la implementación de mis amigos, que nos olvidamos de poner algo de Scope en el método Provider => ocurrirán pérdidas de memoria.
En mi opinión, con una aplicación pequeña (pocas pantallas sin muchas dependencias o con dependencias similares), podríamos aplicar la idea de mis amigos, pero por supuesto que no es recomendable.
Prefiero leer más sobre: ¿Qué determina el ciclo de vida de un componente (gráfico de objeto) en Dagger 2? Alcance de la actividad de Dagger2, ¿cuántos módulos / componentes necesito?
Y una nota más: si desea ver cuándo se destruye el objeto, puede llamar a los del método juntos y el GC se ejecutará inmediatamente:
System.runFinalization();
System.gc();
Si usa solo uno de estos métodos, GC se ejecutará más tarde y puede obtener resultados incorrectos.
ControllerModule
creará uno nuevoPresenter
y luego se inyecta el presentador en elActivity
oFragment
. ¿Alguna opinión sólida a favor o en contra de esto?