What is the use case for @Binds vs @Provides annotation in Dagger2
Asked Answered
H

4

77

I am not certain on the purpose for Dagger2's @Bind annotation.

From what i have read online im still not clear but here is an example:

@Module
public abstract class HomeModule {

  @Binds
  public abstract HomePresenter bindHomePresenter(HomePresenterImp   
    homePresenterImp);
}

and the class definitions look like this:

public interface HomePresenter {
    Observable<List<User>> loadUsers();
}

public class HomePresenterImp implements HomePresenter {

    public HomePresenterImp(){
    }  

    @Override
    public Observable<List<User>> loadUsers(){
        //Return user list observable
    }
}

why would i need to use @Binds if i can just use provides annotation as follows:

@Provides
public HomePresenter provideHomePresenter() {
    return new HomePresenterImp();
}

what is the usecase for @Binds instead of @Provides ? if i use @Binds do i still need to declare it in my appcomponent (its an abstract class when i use @Binds)?

Hurtful answered 1/10, 2018 at 8:7 Comment(2)
If you want to bind your CustomActivity to an AppCompatActivity for example this can only be achievied using Binds annotationCocytus
@SamuelEminet thanks for the reply. could you give an example how it could not be done with provide and how it can be done with binds. i am still confused , what are you binding in this case ? dagger is suppose to bind dependencies , why are you suggesting to bind CustomActivity to an AppCompatActivityHurtful
C
86

@Binds can be perfectly equivalent to a @Provides-annotated method like this:

@Provides
public HomePresenter provideHomePresenter() {
    return new HomePresenterImp();
}

...though you'd probably prefer a variant that takes HomePresenterImp as a method parameter, which lets Dagger instantiate HomePresenterImp (assuming it has an @Inject constructor) including passing any dependencies it needs. You can also make this static, so Dagger doesn't need to instantiate your Module instance to call it.

@Provides
public static HomePresenter provideHomePresenter(HomePresenterImp presenter) {
    return presenter;
}

So why would you choose @Binds instead? Dagger has a FAQ about it, but it boils down do these reasons:

  • @Binds is (slightly) more compact: You can skip the implementation.
  • @Binds works in interfaces and abstract classes, which are strictly required for Dagger features like @BindsOptionalOf and @ContributesAndroidInjector.
  • @Binds helps your code stay efficient. @Provides methods can be instance methods, which require Dagger to instantiate your Module in order to call them. Making your @Provides method static will also accomplish this, but your @Provides method will still compile if you forget the static. @Binds methods will not.
  • @Binds prevents Dagger from having to codegen and keep a separate Factory/Provider for the object, since Java doesn't give Dagger access to know that the implementation is as simple as it is. In your case, Dagger can cast the Provider<HomePresenterImp> to a Provider<HomePresenter> and only keep one, rather than keeping one for HomePresenter that does nothing but call the one for HomePresenterImp.

Thus, the entire thing would be well-represented as:

@Binds abstract HomePresenter bindHomePresenter(HomePresenterImp presenter);
Caesarea answered 3/10, 2018 at 1:39 Comment(5)
hi, can you give a real world example of using binds annotation ?Hurtful
@Hurtful Do you mean an example of the syntax? It shouldn't be hard to find an example of @Binds; it is appropriate for any interface/impl split.Caesarea
i think if you gave me a real world example of being used and compared it to provides it might help me. but yes if not i can keep googleing its just some of the example online are confusing still , obviously why im here lol.Hurtful
@j2emanue: HomePresenterImp is a perfect real-world example. You won't find a clear example of @Binds doing something @Provides CANNOT do, but in some cases @Binds will simply be more compact and efficient about it. If you're having trouble putting it into practice with abstract classes or interfaces, though, check out this question or post a new question with your progress.Caesarea
not clear to me. Still could not find any real use case for @Bind. For the first time, it makes the code harder than expected.Dream
E
9

Thanks to this source: https://www.valueof.io/blog/inject-provides-binds-dependencies-dagger-hilt

@Binds:

  • single param
  • less code

However, the advantage of using @Binds is that it reduces the amount of code generated (such as Module Factory classeenter image description heres). Less code to generate means the Kapt plugin has less work to do which can speed up build times in larger projects.

@Binds is a very specialized annotation though—it’s used to map an interface to an implementation. It can take only a single parameter and the type return is the interface implemented by the given parameter object.

If the implementation you are binding to takes constructor parameters, you can use a combination of @Inject and @Binds as shown in the example below:

Ealdorman answered 25/5, 2022 at 9:42 Comment(0)
C
5

Here a concrete case where you need Bind annotation, imagine you got a BaseActivityModule which is include in all your activity modules that provides your activity viewmodel.

@Module
object BaseActivityModule {
    @Provides
    @ActivityScope
    @ActivityContext
    @JvmStatic
    fun provideViewModelProvider(
        activity: AppCompatActivity,
        viewModelFactory: ViewModelProvider.Factory
    ): ViewModelProvider = ViewModelProviders.of(activity, viewModelFactory)
}

Here you see we need to provide an AppCompatActivity and a ViewModelProvider.Factory. You cannot provide AppCompatActivity with a Provide annotation since activities are created by android.

We're assuming your concrete ActivityModule for example MainActivityModule will provide MainActivity class either because you create a MainActivity sub component or you used ContributesAndroidInjector to automatically create your sub components (but this is another talk).

So we have our MainActivityModule providing MainActivity and our MainActivityModule includes our BaseActivityModule which need an AppCompatActivity. So here the Bind magic, let's tell Dagger that when you need an AppCompatActivity you can use our MainActivity.

@Module(includes = [BaseActivityModule::class])
abstract class MainActivityModule {
    @Binds
    @ActivityScope
    abstract fun bindActivity(activity: MainActivity): AppCompatActivity
}

You can see more from my project template here

Cocytus answered 2/10, 2018 at 9:22 Comment(4)
i was thinking about it further, do you mean that Dagger gets MainActivity reference from the inject call and knows to use it when we declare it with Binds ? so if i call AndroidInjection.inject(this) (where "this" is instance of MainActivity), that it will use that same instance in BaseActivityModule ?Hurtful
appreicate your feedback.Even taking at look at your sample project, specifically the file ApplicationModule.kt ( i see your binding the BeaverApplication) how did Dagger know how to retrieve it ? i see you have a static method to get the application provideApplicationContext(...) then why do you even need binds in that case ?Hurtful
provideApplicationContext could be done with a binding indeed : abstract fun bindApplicationContext(application: BeaverApplication): Context In general when your returning same instance from your passed argument you can use binding instead of providing. And binding is mandatory since dagger only know about concrete class so if your are using interface or abstract classes as providing arguments you'll need to add a binding with those interface/abstract to concrete classesCocytus
Your answer is misleading. You could easily express the latter one with @Provides, and just have bindActivity return its parameter activity. You're right that you don't want to create a new Activity, but Provides implementations can have any logic including simply returning their arguments, which is why @Binds cannot do anything that @Provides can't do.Caesarea
S
2

Dagger gives a great explanation in their FAQ page. Apparently, Dagger already saw this question coming :)

link summary below:

@Provides serves three functions:

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

@Binds methods are a drop-in replacement for Provides methods that simply return an injected parameter. @Binds methods are abstracts methods without implementations

Shaman answered 26/9, 2022 at 1:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.