android – Dagger2 : How to use @Provides and @Binds in same module-ThrowExceptions

Exception or error:

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?

How to solve:

@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.

###

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

###

This is other type solution: Add modules to other module after that you can call top module in your component interface. It can be more efficiency because you can use abstract and static.

Details and examples are below:

For example, we have an component interface and two modules such as ComponentClasses, Module_ClassA and Module_ClassB.

Module_ClassA is:

@Module
public class Module_ClassA {

   @Provides
   static ClassA provideClassA(){

     return new ClassA();
   }
}

Module_ClassB is:

@Module
abstract class Module_ClassB {

   @Binds
   abstract ClassB bindClassB(Fragment fragment); //Example parameter
}

So now, we have two models. If you want use them together, you can add one of them to other. For example: You can add Module_ClassB to Module_ClassA:

@Module(includes = Module_ClassB.class)
public class Module_ClassA {

   @Provides
   static ClassA provideClassA(){

     return new ClassA();
   }
}

Finally, you do not need to add both modules to your component class. You can only add your top module on your component class, like that:

ComponentClasses is:

@Component(modules = Module_ClassA)
public interface ComponentClasses {

   //Example code 
   ArrayList<CustomModel> getList();

}

However, you should be careful because you need to add your top module. Thus, Module_ClassA added on ComponentClasses interface.

@canerkaseler

Leave a Reply

Your email address will not be published. Required fields are marked *