Dagger2中@Binds和@Provides注解的使用场景是什么?

74

我不确定Dagger2的@Bind注解的作用。

根据我在网上阅读的内容,我仍然不清楚,但是这里有一个示例:

@Module
public abstract class HomeModule {

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

类定义如下:

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

public class HomePresenterImp implements HomePresenter {

    public HomePresenterImp(){
    }  

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

如果我可以像以下提供注释那样使用,那么为什么我需要使用@Binds呢:

@Provides
public HomePresenter provideHomePresenter() {
    return new HomePresenterImp();
}
@Binds与@Provides相比,其用途是什么?如果我使用@Binds,是否仍然需要在我的appcomponent中声明它(当我使用@Binds时,它是一个抽象类)?

如果您想将自定义活动(CustomActivity)绑定到AppCompatActivity,例如,只能使用Binds注释来实现。 - Samuel Eminet
2
@SamuelEminet 谢谢回复。你能举个例子说明如何使用绑定而不是提供来完成某些操作吗?我还是有点困惑,你在这种情况下绑定了什么?Dagger应该绑定依赖项,为什么建议将CustomActivity绑定到AppCompatActivity? - j2emanue
4个回答

83

@Binds 可以完全等同于以下使用@Provides 注解的方法:

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

虽然您可能更喜欢一个以HomePresenterImp作为方法参数的变体,这可以让Dagger实例化HomePresenterImp(假设它有@Inject构造函数),包括传递它所需的任何依赖项。您还可以将此设置为static,这样Dagger就不需要实例化您的Module实例来调用它。

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

那么为什么你会选择@Binds呢?Dagger在其FAQ中有解释,但它归结于以下几个原因:

  • @Binds更加紧凑:您可以跳过实现。
  • @Binds适用于接口和抽象类,这是Dagger功能所严格要求的,例如@BindsOptionalOf@ContributesAndroidInjector
  • @Binds有助于使您的代码保持高效。 @Provides方法可以是实例方法,这需要Dagger实例化您的模块才能调用它们。将@Provides方法设为static也可以达到这个目的,但如果您忘记了static,您的@Provides方法仍然可以编译通过。而@Binds方法不行。
  • @Binds可以防止Dagger必须为对象生成和保留单独的工厂/提供程序,因为Java无法让Dagger知道实现如此简单。在您的情况下,Dagger可以将Provider<HomePresenterImp>转换为Provider<HomePresenter>并仅保留一个,而不是保留一个什么都不做的 HomePresenter 的提供程序再去调用HomePresenterImp的提供程序。

因此,整个内容可以完整地表示为:

@Binds abstract HomePresenter bindHomePresenter(HomePresenterImp presenter);

你好,你能给一个使用绑定注解的真实世界例子吗? - j2emanue
@j2emanue 你是指语法的示例吗?找到@Binds的示例应该不难;它适用于任何接口/实现分离的情况。 - Jeff Bowman
我认为如果你给我一个真实世界的例子来使用和比较,那么它可能会帮助我。但是如果没有,我可以继续谷歌搜索,只是一些在线示例仍然令人困惑,显然这就是我在这里的原因。 - j2emanue
@j2emanue:HomePresenterImp是一个完美的现实世界的例子。你不会找到一个清晰的例子,说明@Binds做不了什么@Provides可以做的事情,但在某些情况下,@Binds会更加紧凑和高效。如果你在使用抽象类或接口时遇到困难,可以查看这个问题或发布一个新问题,分享你的进展。 - Jeff Bowman
对我来说不太清楚。我仍然找不到@Bind的任何真实用例。这是第一次,它使代码比预期更难。 - Vahe Gharibyan

9

感谢这个来源:https://www.valueof.io/blog/inject-provides-binds-dependencies-dagger-hilt

@Binds:

  • 单参数
  • 更少的代码

然而,使用@Binds的优点在于它减少了生成的代码量(例如模块工厂类enter image description here)。生成更少的代码意味着Kapt插件需要处理的工作更少,这可以加快在较大项目中的构建时间。

@Binds是一个非常专业的注释——它用于将接口映射到实现。它只能接受一个参数,并且返回类型是给定参数对象实现的接口。

如果您要绑定的实现需要构造函数参数,则可以使用@Inject和@Binds的组合,如下面的示例所示:


5
这里有一个具体案例需要使用Bind注释。假设你有一个名为BaseActivityModule的模块,它包含在所有活动模块中,并提供了活动视图模型。
@Module
object BaseActivityModule {
    @Provides
    @ActivityScope
    @ActivityContext
    @JvmStatic
    fun provideViewModelProvider(
        activity: AppCompatActivity,
        viewModelFactory: ViewModelProvider.Factory
    ): ViewModelProvider = ViewModelProviders.of(activity, viewModelFactory)
}

这里我们需要提供一个AppCompatActivity和一个ViewModelProvider.Factory。由于活动是由android创建的,因此无法使用Provide注释来提供AppCompatActivity

我们假设您具体的ActivityModule例如MainActivityModule将提供MainActivity类,因为您创建了MainActivity子组件或者您使用了ContributesAndroidInjector自动创建了您的子组件(但这是另一个话题)。

所以我们有我们的MainActivityModule提供MainActivity,而我们的MainActivityModule包括我们的BaseActivityModule,它需要一个AppCompatActivity。因此,在这里使用Bind来实现这个“魔法”,让Dagger知道当您需要一个AppCompatActivity时,可以使用我们的MainActivity

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

你可以在我的项目模板这里找到更多信息。

我再仔细想了想,你的意思是Dagger从注入调用中获取MainActivity引用,并且知道在我们使用Binds声明时如何使用它?所以如果我调用AndroidInjection.inject(this)(其中“this”是MainActivity的实例),那么它会在BaseActivityModule中使用同一个实例吗? - j2emanue
感谢您的反馈。即使看了您的示例项目,特别是ApplicationModule.kt文件(我看到您绑定了BeaverApplication),Dagger是如何知道如何检索它的?我看到您有一个静态方法来获取应用程序provideApplicationContext(...),那么在这种情况下,为什么您还需要绑定呢? - j2emanue
provideApplicationContext确实可以通过绑定来完成: abstract fun bindApplicationContext(application: BeaverApplication): Context 通常情况下,当您从传递的参数返回相同的实例时,可以使用绑定而不是提供。并且由于dagger只知道具体类,所以如果您使用接口或抽象类作为提供参数,您需要将这些接口/抽象类与具体类绑定。 - Samuel Eminet
你的回答有误导性。你可以很容易地用@Provides来表达后者,并让bindActivity返回它的参数activity。你是对的,你不想创建一个新的Activity,但是Provides实现可以有任何逻辑,包括简单地返回它们的参数,这就是为什么@Binds不能做到@Provides所能做的任何事情。 - Jeff Bowman

2

Dagger在他们的FAQ页面中给出了很好的解释。显然,Dagger已经预见到了这个问题 :)

以下是链接摘要:

@Provides有三个作用:

  1. 声明提供的类型(可能是限定符)-这是返回类型
  2. 声明依赖项-这些是方法参数
  3. 为实例提供实现方式-这是方法体

@Binds方法是@Provides方法的替代品,它只返回一个注入的参数。@Binds方法是没有实现的抽象方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接