I've been working with dagger2 for a while. And I got confused wether to create an own component/module for each Activity/ Fragment. Please help me clarify this:
For example, We have an app, and the app has about 50 screens. We will implement the code following the MVP pattern and Dagger2 for DI. Suppose that we have 50 activities and 50 presenters.
In my opinion, usually we should organize the code like this :
Create an AppComponent and AppModule which will provide all objects that will be used while the app is open.
@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, ....) }
Create ActivityScope :
@Scope @Documented @Retention(value=RUNTIME) public @interface ActivityScope { }
Create Component and Module for each Activity. Usually I will put them as static classes within the Activity class:
@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.
Those are just very simple examples to show how I would implement this.
But a friend of mine just gave me another implementation:
Create PresenterModule which will provide all presenters:
@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. }
Create AppModule and 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, ....) }
His explaination is: He doesn't have to create components and modules for each activity. I think my friends idea is absolutely not good at all, but please correct me if I am wrong. Here are the reasons:
A lot of memory leaks :
What happens if I want to create two instances of one Activity ? (how can he create two presenters )
It will take a lot of time for the app to initialize (because it has to create many presenters, objects, ...)
Sorry for a long post, but please help me clarify this for me and my friend, I can't convince him. Your comments will be very appreciated.
/-----------------------------------------------------------------------/
Edit after doing a demo.
First, thanks for @pandawarrior answer. I should have created a Demo before I asked this question. I hope my conclusion here could help someone else.
So, all the reasons I have said above are mostly wrong. But it does not mean that we should follow my friend idea, for two reasons:
It's not good for the source's architecture, when he inits all presenters in module / component. (It violates Interface segregation principle, maybe Single Responsibility priciple, too).
When we create a Scope Component, we will know when it's created and when it's destroyed which is a huge benefit for avoiding memory leaks. So, for each Activity we should create a Component with an @ActivityScope. Let's imagine, with my friends implementation, that we forgot to put some Scope in the Provider-method => memory leaks will occur.
In my opinion, with a small app (just a few screens without many dependencies or with similar dependencies), we could apply my friends idea, but of course it's not recommended.
Prefer to read more on: What determines the lifecycle of a component (object graph) in Dagger 2? Dagger2 activity scope, how many modules/components do i need?
And one more note: If you want to see when the object are destroyed, you can call those of method together and the GC will run immediately:
System.runFinalization(); System.gc();
If you use only one of these methods, GC will run later, and you may get wrong results.
Now Component in a Dagger works by creating a graph of all the dependencies in the project so that it can find out where it should get those dependencies when they are needed. In order to implement this, an interface needs to be created and should be annotated with @Component.
It defines a configuration point for your object graph, where you declare which objects you want to be available for injection and their scopes. As a simple example, let's say we want a singleton object to be used by any Activity in the app.
Dagger is implemented using Java's annotations model. It generates code at compile-time using an annotation processor. Annotation processors are supported in Kotlin with the kapt compiler plugin. They are enabled by adding id 'kotlin-kapt' to the top of the file below the id 'kotlin-android-extensions' line.
Declaring a separate module for each Activity
is not a good idea at all. Declaring separate component for each Activity
is even worse. The reasoning behind this is very simple - you don't really need all these module/components (as you have already seen by yourself).
However, having just one component that is tied to Application
's life-cycle and using it for injection into all Activities
is also not the optimal solution (this is your friend's approach). It is not optimal because:
@Singleton
or a custom one)Services
too, but Services
can require different objects than Activities
(e.g. Services
don't need presenters, don't have FragmentManager
, etc.). By using a single component you loose the flexibility of defining different object graphs for different components.So, a component per Activity
is an overkill, but single component for the entire application is not flexible enough. The optimal solution is in between these extremes (as it usually is).
I use the following approach:
Application
.Activities
and Fragments
). Instantiated in each Activity
and Fragment
.Services
. Instantiated in each Service
.Following is an example of how you could implement the same approach.
Edit July 2017
I published a video tutorial that shows how to structure Dagger dependency injection code in Android application: Android Dagger for Professionals Tutorial.
Edit February 2018
I published a complete course about dependency injection in Android.
In this course I explain the theory of dependency injection and show how it emerges naturally in Android application. Then I demonstrate how Dagger constructs fit into the general dependency injection scheme.
If you take this course you will understand why the idea of having a separate definition of module/component for each Activity/Fragment is basically flawed in the most fundamental way.
Such an approach causes the structure of presentation layer from "Functional" set of classes to be mirrored into the structure of "Construction" set of classes, thus coupling them together. This goes against the main objective of dependency injection which is to keep the "Construction" and "Functional" sets of classes disjoint.
Application scope:
@ApplicationScope @Component(modules = ApplicationModule.class) public interface ApplicationComponent { // Each subcomponent can depend on more than one module ControllerComponent newControllerComponent(ControllerModule module); ServiceComponent newServiceComponent(ServiceModule module); } @Module public class ApplicationModule { private final Application mApplication; public ApplicationModule(Application application) { mApplication = application; } @Provides @ApplicationScope Application applicationContext() { return mApplication; } @Provides @ApplicationScope SharedPreferences sharedPreferences() { return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE); } @Provides @ApplicationScope SettingsManager settingsManager(SharedPreferences sharedPreferences) { return new SettingsManager(sharedPreferences); } }
Controller scope:
@ControllerScope @Subcomponent(modules = {ControllerModule.class}) public interface ControllerComponent { void inject(CustomActivity customActivity); // add more activities if needed void inject(CustomFragment customFragment); // add more fragments if needed void inject(CustomDialogFragment customDialogFragment); // add more dialogs if needed } @Module public class ControllerModule { private Activity mActivity; private FragmentManager mFragmentManager; public ControllerModule(Activity activity, FragmentManager fragmentManager) { mActivity = activity; mFragmentManager = fragmentManager; } @Provides @ControllerScope Context context() { return mActivity; } @Provides @ControllerScope Activity activity() { return mActivity; } @Provides @ControllerScope DialogsManager dialogsManager(FragmentManager fragmentManager) { return new DialogsManager(fragmentManager); } // @Provides for presenters can be declared here, or in a standalone PresentersModule (which is better) }
And then in Activity
:
public class CustomActivity extends AppCompatActivity { @Inject DialogsManager mDialogsManager; private ControllerComponent mControllerComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getControllerComponent().inject(this); } private ControllerComponent getControllerComponent() { if (mControllerComponent == null) { mControllerComponent = ((MyApplication)getApplication()).getApplicationComponent() .newControllerComponent(new ControllerModule(this, getSupportFragmentManager())); } return mControllerComponent; } }
Additional information on dependency injection:
Dagger 2 Scopes Demystified
Dependency Injection in Android
Some of the best examples of how to organise your components, modules, and packages can be found in the Google Android Architecture Blueprints Github repo here.
If you examine the source code there, you can see there is one single app-scoped Component (with a lifecycle of the duration of the whole app) and then separate Activity-scoped Components for the Activity and Fragment corresponding to a given functionality in a project. For example, there are the following packages:
addedittask taskdetail tasks
Inside each package there is a module, component, presenter etc. For instance, inside taskdetail
there are the following classes:
TaskDetailActivity.java TaskDetailComponent.java TaskDetailContract.java TaskDetailFragment.java TaskDetailPresenter.java TaskDetailPresenterModule.java
The advantage of organising this way (rather than grouping all of the activities in one component or module) is that you can take advantage of Java accessibility modifiers and fulfil Effective Java item 13. In other words, the functionally grouped classes will be in the same package and you can take advantage of protected
and package-private
accessibility modifiers to prevent unintended usages of your classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With