Dagger2 : How to use @Provides and @Binds in same

2019-04-23 14:39发布

问题:

I'm using the new Dagger2 (ver 2.11) and I'm using the new features like AndroidInjector, and ContributesAndroidInjector. I have an activity subcomponent,

        @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();

}

And the UserListModule provides dependencies for the fragment. Some of the dependencies I just want to bind the instances , and return , like

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

Instead of simply just return the dependency , like

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

My module contains some @Provides methods as well. Can we use both @Binds and @Provides methods in the same module? I tried as shown below

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

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

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

        }

And it its throwing error

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.

Is there any way to do this?

回答1:

@Binds and @ContributesAndroidInjector methods must be abstract, because they don't have method bodies. That means that they must go on an interface or abstract class. @Provides methods may be static, which means they can go on abstract classes and Java-8-compiled interfaces, but non-static ("instance") @Provides methods don't work on abstract classes. This is explicitly listed in the Dagger FAQ, under the sections "Why can’t @Binds and instance @Provides methods go in the same module?" and "What do I do instead?".

If your @Provides method doesn't use instance state, you can mark it static, and it can go onto an abstract class adjacent to your @Binds methods. If not, consider putting the bindings like @Binds and @ContributesAndroidInjector into a separate class--possibly a static nested class--and including that using the includes attribute on Dagger's @Module annotation.



回答2:

A little addition to Jeff's solution above:

you may create inner interface instead of static inner class, like this:

@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);
    }
}