Estou trabalhando com o dagger2 há algum tempo. E fiquei confuso se deveria criar um componente / módulo próprio para cada atividade / fragmento. Por favor me ajude a esclarecer isso:
Por exemplo, temos um aplicativo, e o aplicativo tem cerca de 50 telas. Implementaremos o código seguindo o padrão MVP e Dagger2 para DI. Suponha que temos 50 atividades e 50 apresentadores.
Na minha opinião, normalmente devemos organizar o código assim:
Crie um AppComponent e AppModule que fornecerá todos os objetos que serão usados enquanto o aplicativo estiver aberto.
@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, ....) }
Criar ActivityScope:
@Scope @Documented @Retention(value=RUNTIME) public @interface ActivityScope { }
Crie um componente e um módulo para cada atividade. Normalmente, vou colocá-los como classes estáticas dentro da classe 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.
Esses são apenas exemplos muito simples para mostrar como eu implementaria isso.
Mas um amigo meu acabou de me dar outra implementação:
Crie PresenterModule que fornecerá a todos os apresentadores:
@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. }
Crie AppModule e 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, ....) }
Sua explicação é: ele não precisa criar componentes e módulos para cada atividade. Acho que a ideia dos meus amigos não é nada boa, mas corrija-me se eu estiver errado. Aqui estão as razões:
Muitos vazamentos de memória :
- O aplicativo criará 50 apresentadores, mesmo se o usuário tiver apenas 2 atividades abertas.
- Depois que o usuário fecha uma atividade, seu apresentador ainda permanecerá
O que acontece se eu quiser criar duas instâncias de uma atividade? (como ele pode criar dois apresentadores)
Levará muito tempo para o aplicativo inicializar (porque ele precisa criar muitos apresentadores, objetos ...)
Desculpe pelo longo post, mas por favor me ajude a esclarecer isso para mim e para meu amigo, não consigo convencê-lo. Seus comentários serão muito apreciados.
/ ------------------------------------------------- ---------------------- /
Edite depois de fazer uma demonstração.
Primeiramente, obrigado pela resposta @pandawarrior. Eu deveria ter criado uma demonstração antes de fazer esta pergunta. Espero que minha conclusão aqui possa ajudar outra pessoa.
- O que meu amigo fez não causa vazamentos de memória, a menos que ele coloque qualquer Scope nos métodos Provides. (Por exemplo @Singleton ou @UserScope, ...)
- Podemos criar muitos apresentadores, se o método Provides não tiver nenhum Scope. (Então, meu segundo ponto também está errado)
- Dagger criará os apresentadores somente quando eles forem necessários. (Portanto, o aplicativo não demorará muito para inicializar, fiquei confuso com a injeção lenta)
Portanto, todas as razões que disse acima estão erradas. Mas isso não significa que devemos seguir a ideia do meu amigo, por dois motivos:
Não é bom para a arquitetura do código-fonte, quando ele inicia todos os apresentadores no módulo / componente. (Isso viola o princípio de segregação de interface , talvez o princípio de responsabilidade única também).
Quando criamos um componente de escopo, saberemos quando ele é criado e quando é destruído, o que é um grande benefício para evitar vazamentos de memória. Portanto, para cada Activity devemos criar um Component com um @ActivityScope. Vamos imaginar, com a implementação de meus amigos, que esquecemos de colocar algum escopo no método Provider => vazamentos de memória irão ocorrer.
Na minha opinião, com um aplicativo pequeno (apenas algumas telas sem muitas dependências ou com dependências semelhantes), poderíamos aplicar a ideia do meu amigo, mas é claro que não é recomendável.
Prefira ler mais sobre: O que determina o ciclo de vida de um componente (gráfico de objeto) no Dagger 2? Escopo da atividade Dagger2, quantos módulos / componentes eu preciso?
E mais uma observação: se você quiser ver quando os objetos são destruídos, você pode chamar aqueles do método juntos e o GC será executado imediatamente:
System.runFinalization();
System.gc();
Se você usar apenas um desses métodos, o GC será executado mais tarde e você poderá obter resultados errados.
ControllerModule
irá criar um novoPresenter
e então o apresentador é injetado noActivity
ouFragment
. Alguma opinião sólida a favor ou contra isso?