Dagger 2 Annotations: @Binds & @ContributesAndroidInjector

Garima Jain
ProAndroidDev
Published in
5 min readNov 8, 2017

--

This article is a part of the “Dagger and the Dahaka” series. In this article we will briefly look at two annotations : @Binds and @ContributesAndroidInjector. It is not mandatory to have read the previous articles to understand this, but a basic understanding of Dagger is a must.

Dagger has many helpful Annotations which can not only reduce the code you write, but also make your generated code much more optimized and one of those annotations is @Binds.

Binds

This annotation provides a replacement of @Provides methods which simply return the injected parameter. Let’s take an example,

We have a LoginPresenter which implements LoginContract.Presenter . Without @Binds, the provider method for it will be something as follows :

@Provides
public LoginContract.Presenter
provideLoginPresenter(LoginPresenter loginPresenter) {
return loginPresenter;
}

In the above case, we can instead use @Binds annotation and make the above method abstract:

@Binds
public abstract LoginContract.Presenter
provideLoginPresenter(LoginPresenter loginPresenter);

Of course, we will also need to mark our Module as abstract in this case, which is more efficient than a concrete one and thus makes @Binds more efficient.

What makes our abstract Module more performant?

@Provides methods are instance methods and they need an instance of our module in order to be invoked. If our Module is abstract and contains @Binds methods, dagger will not instantiate our module and instead directly use the Provider of our injected parameter (LoginPresenter in the above case).

What if your module contains both @Provides and @Binds methods?

Dagger documentation has great FAQ questions on @Binds vs @Provides methods. In case, your Module has both @Provides and @Binds methods, you have two options :

  1. Simplest would be to mark your @Provides instance methods as static.
  2. If it is necessary to keep them as instance methods, then you can split your module into two and extract out all the @Binds methods into an abstract Module.

To know about more such optimizations and even others which the recent version 2.12 of dagger has brought along with it. I recommend watching “Optimizing Dagger on Android” by Ron Shapiro, Google.

@ContributesAndroidInjector

If you have seen a basic implementation of dagger-android, you would know that it introduces some amount of boilerplate code. When you are trying to switch to dagger-android or converting your current app to use it. The most cumbersome I think is having to create separate sub-components for each of your Activities, Fragments, Services, etc. and adding those to the injectorFactories of your DispatchingAndroidInjector (by using @IntoSet)

Dagger Android introduced an annotation which can reduce the Component Binds IntoSet Subcomponent ActivityKey FragmentKey etc. boilerplate for you.

If you have a simple module like the following, you can then let dagger handle the rest.

@Module
public abstract class LoginModule {

@Binds
public abstract LoginContract.Presenter
provideLoginPresenter(LoginPresenter loginPresenter);

}

All you need to do is write the following snippet inside the Module of the component, which is going to be the super-component of the generated LoginComponent. Example, If you have an AppComponent and you want dagger to generate a LoginSubcomponent for your LoginActivity, you will write the following snippet inside your AppModule.

@ContributesAndroidInjector(modules = LoginModule.class)
abstract LoginActivity loginActivity();

It’s a good practice to extract out a separate ActivityBindingModule for all such bindings and include it in the modules list of your AppComponent as follows :

@Singleton
@Component(modules = {
AppModule.class,
ActivityBindingModule.class})
public interface AppComponent

Now, let’s refer to the documentation of @ContributesAndroidInjector and figure out what’s going on here :

  • “Generates an AndroidInjector for the return type of this method” : It will generate AndroidInjector<LoginActivity>. (More about it in upcoming posts)
  • This annotation must be applied to an abstract method in a Module that returns a concrete Android framework type.” i.e. Activity / Fragment, etc.

Following is the boilerplate that gets generated for you when you use @ContributesAndroidInjector:

//Simplified Generated File@Module(subcomponents =
Module_LoginActivity.LoginActivitySubcomponent.class)
public abstract class Module_LoginActivity {
private Module_LoginActivity() {}

@Binds
@IntoMap
@ActivityKey(LoginActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindAndroidInjectorFactory(
LoginActivitySubcomponent.Builder builder);

@Subcomponent(modules = LoginModule.class)
public interface LoginActivitySubcomponent extends
AndroidInjector<LoginActivity> {
@Subcomponent.Builder
abstract class Builder extends
AndroidInjector.Builder<LoginActivity> {}
}
}
  1. It generates the LoginActivitySubcomponent.
  2. It adds the necessary @Subcomponent annotation for us.
  3. It adds an entry of (LoginActivity.class, LoginActivitySubcomponent.Builder) to the Map of Injector Factories used by DispatchingAndroidInjector . Dagger-Android uses this entry to build our LoginActivitySubcomponent and perform injections for LoginActivity.
  4. Also, binds LoginActivity to the object-graph. (See the source code of AndroidInjector to know how, Hint : @BindsInstance)

Similarly, you can also use @ContributesAndroidInjector with any of the android framework types like Fragments, Services, etc. Here’s an example using Fragments.

Scoping with @ContributesAndroidInjector

If you want to scope dependencies inside of your LoginModule, then you need your generated LoginActivitySubcomponent to be scoped as well. Learn more about scopes here. To do this, you can specify a scope along with your @ContributesAndroidInjector annotation and that will let you scope anything inside your LoginModule as follows.

@ContributesAndroidInjector(modules = LoginModule.class)
@ActivityScope
abstract
LoginActivity loginActivity();

This will generate your LoginActivitySubcomponent annotated with an ActivityScope.

//Simplified Generated File@Module(subcomponents =
Module_LoginActivity.LoginActivitySubcomponent.class)
public abstract class Module_LoginActivity {
...

@Subcomponent(modules = LoginModule.class)
@ActivityScope
public interface LoginActivitySubcomponent extends
AndroidInjector<LoginActivity> {
@Subcomponent.Builder
abstract class Builder extends
AndroidInjector.Builder<LoginActivity> {}
}
}

Now that the Component is scoped, you can apply the general rules of scoping and mark any of your @Binds methods with @ActivityScope :

@Binds
@ActivityScope
public abstract LoginContract.Presenter provideLoginPresenter(LoginPresenter loginPresenter);

That’s it :)

As you can see in the above case, by understanding what’s going on under the hood we are trying to befriend the beast of generated code (Dahaka) which in-turn helps us optimize our generated code and reduce boilerplate.

In the upcoming articles we will try to learn more about dagger-android and what goes on behind the scenes. If you would like to see an implementation of dagger-android you can have a look the the Dahaka github repository here.

So just Befriend the Beast and use Dagger-Android.

Special Thanks to Mike Nakhimovich for reviewing 🙂 and suggesting the edits.

I am myself an Android Developer getting chased by “Dahaka” every day. While going through this series of blog posts if you think that something can be done in a better way, Kindly share it so that we can all befriend the beast together.

<< Main Article

Stay tuned for the whole series 🙂

--

--