Search code examples
androidmvpdagger-2dagger

Setting Modules and Components with Dagger 2 for Android MVP - Activity with multiple fragments


I have been working with Dagger 2 for a while and I m still trying to figure some things out. One thing that I still don`t manage very good is setting up modules and components for different situations, like an activity with several fragments. I have seen a lot of implementations and most of the time it is a bit different.

So, let me expose my current app structure which uses MVP and I would like some opinions if my implementation is ok or not.

@Module
public final class ApplicationModule {

private Context mContext;


public ApplicationModule(Context context){
    mContext = context;
}


public ApplicationModule(){
    mContext = null;
}

@Provides
Context provideContext(){
    return mContext;
}

@Singleton
@Provides
public SharedPreferences getAppPreferences(){
    return mContext.getSharedPreferences("CalorieApp",Context.MODE_PRIVATE);
 }
}

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

void inject(MainApplication mainApplication);

SharedPreferences sharedPreferences();

}

In this AppModule I will usually set only Singleton that my app will need. Like SharedPreferences or anything related to network requests. This Module and Component are somehow standard and I always start my apps with creating them like this.

Then I set my module and component for Activity which will have a dependencie on ApplicationComponent

@Module
public class ActivityModule {

private Activity activity;

public ActivityModule(Activity activity){
    this.activity = activity;
}

@Provides
Activity provideActivity(){
    return  activity;
 }
}

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = 
  ActivityModule.class)
public interface ActivityComponent {

void inject(WelcomeActivity welcomeActivity);

void inject(MainActivity mainActivity);
}

Now, MainActivity has 3 fragments and I will create 3 modules for fragments and 1 component

@Module
public class HomeFragmentModule {

private HomeFragmentContract.View mView;

public HomeFragmentModule(HomeFragmentContract.View view){
    mView = view;
}

@Provides
HomeFragmentContract.View provideHomeFragmentView(){
    return mView;
  }

}

@Module
public class ChartsFragmentModule {

private ChartsFragmentContract.View mView;

public ChartsFragmentModule(ChartsFragmentContract.View view){
    mView = view;
}

@Provides
ChartsFragmentContract.View provideChartsFragmentView(){
    return mView;
}
}

@Module
public class ProfileFragmentModule {

private ProfileFragmentContract.View mView;

public ProfileFragmentModule(ProfileFragmentContract.View view){
    mView = view;
}

@Provides
ProfileFragmentContract.View provideProfileFragmentContract(){
    return mView;
}

}

@PerFragment
@Component(dependencies = ActivityComponent.class ,
    modules = {ChartsFragmentModule.class, HomeFragmentModule.class, 
ProfileFragmentModule.class})
public interface FragmentComponent {

void inject(ChartsFragment chartsFragment);

void inject(HomeFragment homeFragment);

void inject(ProfileFragment profileFragment);
}

Then I will have to instantiate Dagger, first in my application class and then in each of the activities and fragments

applicationComponent = DaggerApplicationComponent.builder()
            .applicationModule(new ApplicationModule(this))
            .build();

For example in WelcomeActivity, I instantiate it like this:

    DaggerActivityComponent.builder()
            .activityModule(new ActivityModule(this))
            .applicationComponent(((MainApplication) 
getApplication()).getApplicationComponent())
            .build()
            .inject(this);

In MainActivity I do it same as above, but I`m creating a getter for activity component inside it.

Then in each of my fragments I instantiate like this:

    DaggerFragmentComponent.builder()
            .homeFragmentModule(new HomeFragmentModule(this))              
    .activityComponent(((MainActivity)getActivity()).getActivityComponent())
            .build()
            .inject(this);

At this point everything works fine. I can inject Presenters and anything I want, but I`m not sure if it is the correct approach.

What do you think of my implementation?

Also I have one Repository class that will be used in every Presenter to display information from Firebase to the UI .

Would you create a component and module just for that and then make all the fragments have a dependency to it?

Hope I did not ask too many questions, but I would really like to clean my ideas.

Thank you


Solution

  • Your setup is quite good. Definitely among the best ones I saw.

    I would like to suggest few minor changes that will make your life easier in the long run.

    Subcomponents instead of components dependencies:

    I'm yet to see a use case where specifying dependencies between components is better than using subcomponents.

    Subcomponents have direct access to the entire object graph of the parent component. This has two advantages:

    1. makes it easy to make use of the objects that parent provides (zero code)
    2. makes it easy to change object's scope (e.g. move some object to Application component in order to make it global)

    A general principle of convention over configuration applies in this case in favor of subcomponents.

    No need for differentiation between Activities and Fragments:

    I noticed that object graph required by Activities usually very similar to the one required by Fragments.

    In my MVC/MVP approach, I designate Activities and Fragments as controllers, and have a single ControllerComponent that is used in order to inject dependencies into controllers.

    However, even using another approach to MVC/MVP (or none at all), if you think about them conceptually - Activities and Fragments have very similar functions.

    Therefore, I suggest having one single component that injects both Activities and Fragments.

    No need for a module per Fragment:

    I have already answered a question about having one component/module per Activity/Fragment here. Please read that answer - it also provides code examples which are relevant to this, and all the above suggestions.

    IMHO, modules should group dependencies by problem domain, not by the component that will use them. For example, an e-commerce app could have the following modules: NetworkingModule, CurrencyModule, CartModule, CheckoutModule, etc...

    Real implementation example:

    I have open sourced my own application some time ago, and, IMHO, it has relatively good dependency injection structure. You can review this structure here.

    One aspect of this DI structure that I'm not satisfied with yet is that not all dependencies are being distributed in modules by domain. You can do better than that.

    Subscribe to my blog:

    I feel a bit uncomfortable to plug this here, but we're recording an advanced video tutorial on Dagger right now. We've been working on this tutorial for the past month and it should be ready in 2-3 weeks.

    This tutorial will discuss exactly what you asked for - how to structure Dagger code for maintainability. You can subscribe to my blog at www.techyourchance.com in order to be notified when it is released.

    Hope this helped.