Dagger 2: can @Provides and @Binds coexist?

Theoretically, Dagger modules cannot simultaneously contain abstract and non-abstract methods. But that doesn’t stop us from finding alternative solutions, right?


Before moving into more details, let’s establish a couple of reasonable requirements.

Prerequisites:

  • Basic experience with Android and Java development.
  • Minimal understanding of how Dagger 2 works.

Alright, let’s go!

What is @Provides annotation?

In Dagger universe, @Providesis the most common construct for configuring a binding. Basically, the returned objects from annotated methods are available for dependency injection. Methods annotated with this annotation can also express dependencies via method parameters.

As Dagger FAQ states, @Provides serves three functions:

  • Declare which type (possibly qualified) is being provided — this is the return type
  • Declare dependencies — these are the method parameters
  • Provide an implementation for exactly how the instance is provided — this is the method body

Note:@Provides annotated methods require a concrete class in order to construct an instance on which the method can be invoked.

Too see how this annotation is used, let’s take this simple RegistrationModule example that provides the presenter for a registration screen:

What is @Binds annotation and why should we use it?

This annotation is pretty much a substitute for @Providesin the sense that it simply returns an injected parameter.

Also, @Binds is preferred because the generated implementation is likely to be more efficient. But why?

Remember we stated that a @Providesannotated method provides an implementation for exactly how the instance is provided? This action can often be laborious and repetitive. So, whenever there is a @Provides whose implementation is simple and common enough to be inferred by Dagger, it makes sense to just declare that as a method without a body (an abstract method) and have Dagger apply the behavior. And that’s how @Binds was born!

Now, methods annotated with @Binds or @ContributesAndroidInjector must be abstract as they have no bodies. With this in mind, both must be part of a interface or any other abstract class.

Let’s adapt our previous example and see how @Binds annotation is used:

This looks much cleaner! And interesting enough, our abstract module is much more faster. Again, but why?

@Provides annotated methods are instance methods — they have an implementation that will be invoked at some point. RegistrationModule is now abstract and therefore constituted only of abstract methods — no implementation is ever created and nothing is ever invoked, so Dagger will not instantiate the module but instead will use the Providers of the injected parameters, in this case RegistrationPresenter .

Now that we clarified these matters, let’s get back to the main question.

How can we then have @Provides and @Binds methods in the same module?

Let’s assume we need to append to our module a simple method that provides a string from the activity’s intent as below:

As this method is not an instance method, we can just mark it as static and we’re good to go! Also, as well as being compatible with @Binds, static methods often perform better than instance methods.

Our module would now look like this:

Now the following question rises, what do we do if we are forced to add an instance method to our abstract module?

Dagger FAQ comes to the rescue again! The only way to solve this issue is to separate our @Provides methods and @Binds methods into two separate modules and include one from the other.

Our module now will be a hybrid non-abstract class that contains an inner abstract class. Let’s see how this would look like in the case of a ServletRequestModule that contains an instance method.

All you have to do is create an inner interface annotated as a module within a non-abstract one and then deposit all your @Binds methods there.

In the above example, ServletRequestAbstractModule has been included in the main non-abstract ServletRequestModule, and so all the abstract methods that were needed do not interfere with your instance methods.

Conclusion

The article presented a short summary on how@Provides and @Binds annotated methods work and also how we can squeeze them in the same Module even though they are of opposite types.

You should now be able to understand the differences of the two annotations and in what circumstances you can include these two contrasting annotations in the same module.

That’s it, we’re done!

Remember, if you liked this post subscribe for more by using the bottom subscribe widget! And if you really enjoyed it, then you can buy me a coffee here! Thanks!

9+
%d bloggers like this: