【问题标题】:Dagger2 : How to use @Provides and @Binds in same moduleDagger2:如何在同一个模块中使用@Provides 和@Binds
【发布时间】:2017-10-07 09:42:07
【问题描述】:

我正在使用新的 Dagger2(2.11 版),并且正在使用 AndroidInjectorContributesAndroidInjector 等新功能。我有一个活动子组件,

        @Module
        abstract class ActivityBuilderModule {
            @ContributesAndroidInjector(modules = 
                   {UserListModule.class, MainFragmentModule.class})
            @ActivityScope
            abstract MainActivity bindsMainActivity();

        }



  @Module
  public abstract class MainFragmentModule {
    @ContributesAndroidInjector
    @FragmentScope
    @FragmentKey(UserListFragment.class)
    abstract UserListFragment bindsUserListFragment();

}

UserListModule 为片段提供了依赖项。一些依赖我只想绑定实例,然后返回,比如

 @Binds
 @ActivityScope
 abstract UserListView mUserListView(UserListFragment userListFragment);

而不是简单地返回依赖,比如

@Provides
@ActivityScope
UserListView mUserListView(UserListFragment userListFragment){
    return userListFragment;
}

我的模块也包含一些@Provides 方法。我们可以在同一个模块中同时使用@Binds@Provides 方法吗?我试过如下图

        @Module
        public abstract class UserListModule {
            @Provides
            @ActivityScope
            UserListFragment mUserListFragment() {
                return new UserListFragment();
            }

            @Binds
            @ActivityScope
            abstract UserListView mUserListView(UserListFragment userListFragment);

           // other provides and binds methods...
           ......
           .....

        }

它的抛出错误

Error:(22, 8) error: dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.

有什么办法吗?

【问题讨论】:

  • 您确实意识到您添加到片段管理器的片段将被系统重新创建,并且在进程死亡后可能不会在模块中,对吧?
  • 你的意思是我不应该限定片段?能不能说的清楚点?我没有正确理解你
  • 进程死亡后,片段将由系统初始化,而不是由该模块初始化。所以你最终可能会得到它的 2 个实例。
  • 好的,但实际问题呢? 我们可以在同一个模块中同时使用 Binds 和 Provides 方法吗?
  • 我认为@Binds + @Provides 应该可以工作,但我不知道ContributesAndroidInjector 是如何影响它的。

标签: android dagger-2


【解决方案1】:

@Binds@ContributesAndroidInjector 方法必须是抽象的,因为它们没有方法体。这意味着它们必须使用接口或抽象类。 @Provides 方法可能是 static,这意味着它们可以继续用于抽象类和 Java-8 编译的接口,但非静态(“实例”)@Provides 方法不适用于抽象类。这在 Dagger 常见问题解答中明确列出,位于 "Why can’t @Binds and instance @Provides methods go in the same module?""What do I do instead?" 部分下。

如果您的@Provides 方法不使用实例状态,您可以将其标记为static,它可以进入与您的@Binds 方法相邻的抽象类。如果没有,请考虑将 @Binds@ContributesAndroidInjector 之类的绑定放入单独的类(可能是静态嵌套类)中,并包括在 Dagger 的 @Module 注释上使用 includes 属性。

【讨论】:

    【解决方案2】:

    对上面 Jeff 解决方案的一点补充:

    您可以创建内部接口而不是静态内部类,如下所示:

    @Module(includes = AppModule.BindsModule.class)
    public class AppModule {
        // usual non-static @Provides
        @Provides
        @Singleton
        Checkout provideCheckout(Billing billing, Products products) {
            return Checkout.forApplication(billing, products);
        }
        // interface with @Binds
        @Module
        public interface BindsModule {
            @Binds
            ISettings bindSettings(Settings settings);
        }
    }
    

    【讨论】:

    • 除了删除抽象关键字样板之外,使用接口而不是抽象类是否有优点/缺点?
    • 除了删除抽象关键字样板之外,使用接口而不是抽象类是否有优点/缺点?
    【解决方案3】:

    如果您在 kotlin 世界,另一种选择是利用 companion object

    @Module
    abstract class MyDaggerModule {
    
        @Binds
        abstract fun provideSomething(somethingImpl: SomethingImpl): Something
    
        companion object {
    
            @Provides
            fun provideAnotherThing(): AnotherThing {
                return AnotherThing()
            }
        }
    }
    

    【讨论】:

    • 多绑定不行吗?
    【解决方案4】:

    这是其他类型的解决方案:将模块添加到其他模块之后,您可以在组件界面中调用顶部模块。它可以更高效,因为您可以使用抽象和静态。

    详情及示例如下:

    比如我们有一个组件接口和两个模块,分别是ComponentClassesModule_ClassAModule_ClassB

    Module_ClassA 是:

    @Module
    public class Module_ClassA {
    
       @Provides
       static ClassA provideClassA(){
    
         return new ClassA();
       }
    }
    

    Module_ClassB 是:

    @Module
    abstract class Module_ClassB {
    
       @Binds
       abstract ClassB bindClassB(Fragment fragment); //Example parameter
    }
    

    所以现在,我们有两个模型。如果您想一起使用它们,您可以将其中一个添加到另一个。例如:您可以将 Module_ClassB 添加到 Module_ClassA

    @Module(includes = Module_ClassB.class)
    public class Module_ClassA {
    
       @Provides
       static ClassA provideClassA(){
    
         return new ClassA();
       }
    }
    

    最后,您不需要将这两个模块都添加到您的组件类中。您只能在组件类中添加顶级模块,如下所示:

    组件类是:

    @Component(modules = Module_ClassA)
    public interface ComponentClasses {
    
       //Example code 
       ArrayList<CustomModel> getList();
    
    }
    

    但是,您应该小心,因为您需要添加顶层模块。因此,在 ComponentClasses 接口上添加了 Module_ClassA。

    【讨论】:

      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多