Dagger 2 - how to avoid code repetition for inject

2019-03-06 05:33发布

问题:

The project I'm working on has a number of utility classes that require activity context.

I don't want to have to declare a new @Provides method for each activity that uses the dependency. i.e. I don't want this:

@Provides
static Navigator providesNavigator(ActivityOne activity) {
    returns new Navigator(activity);
}

// ...and in another module

@Provides
static Navigator providesNavigator(ActivityTwo activity) {
   returns new Navigator(activity);
}

So instead I declare these utilities in a single ActivityUtilitiesModule and pass our BaseActivity which all other activities extend. Now i don't have to declare my Navigator dependency x number of times.

@Provides
static Navigator(BaseActivity activity) {
    return new Navigator(activity);
}

However, Dagger does not know how to satisfy the dependency for BaseActivity. This means for every Activity i need to create a provides method that will satisfy the BaseActivity dependency with the specific Activity being used. e.g.:

@Provides
static BaseActivity providesBaseActivity(ActivityOne activity) {
    return activity;
}

This is better - I only need to repeat this one provider per activity, rather than repeating a provider for every utility class per activity, but it still feels like an unwelcome additional step in Dagger set up, and another thing that makes the code harder to understand.

Is there a pattern which allows me to avoid having to supply this BaseActivity provider per activity?

回答1:

Please use Constructor Injection. Having provide* methods that only call the constructor are only noise and code to maintain. Add @Inject to the class constructor and possible scopes onto the class.

@SomePossibleScope
class Navigator {
  @Inject
  Navigator(Activity activity) { }
}

If you do this, you probably don't need your ActivityUtilitiesModule at all.


If your class depends on an Activity or BaseActivity then you need to provide it. And yes, you will have to tell Dagger about it in some way.

If you were to use an abstract class or interface you should make use of @Binds instead.

@Binds BaseActivity bindsBaseActivity(ActivityOne activity);

Compared to @Provides Dagger might optimize this code further, reducing the number of method calls and object creations, as well as a few less lines of code.


I don't know why your Utils depend on the Activity, but if they would only need a Context then you could also just provide the application context to them without a need to bind or provide your actual Activity.

I personally just bind the current Activity to the types it implements using the syntax above. And if you're using Constructor Injection properly that's more often than not the only lines of code that you'll find in my modules, making them very readable.