49

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?

  • You do realize that fragments you add to the fragment manager will be recreated by the system, and probably won't be in the Module after process death, right? – EpicPandaForce Oct 07 '17 at 09:47
  • You meant I should not Scope the fragment? Can you be more clear, please? I didn't get you properly –  Oct 07 '17 at 09:51
  • 1
    After a process death, the fragment will be initialized by the system, and not by this module. So you'll likely end up with 2 instances of it. – EpicPandaForce Oct 07 '17 at 09:58
  • Okay but what about the actual question? _Can we use both Binds and Provides methods in the same module?_ –  Oct 07 '17 at 10:06
  • I think `@Binds` + `@Provides` should work, but I don't know how `ContributesAndroidInjector` affects it. – EpicPandaForce Oct 07 '17 at 10:08
  • And can you please elaborate on this _After a process death, the fragment will be initialized by the system, and not by this module. So you'll likely end up with 2 instances of it_. The reason I used this is, my `UserListView` has to return the same fragment instance. Did you mean returning a fragment from a module should be avoided? Fragment should not be a part of the dagger dependency graph? It would be helpful if you could explain on this, because still it's not been digested for me since I'm a beginner. –  Oct 07 '17 at 10:18
  • @EpicPandaForce And there is an edit made in my question. I wrongly pasted the code before. Please see my updated code in question –  Oct 07 '17 at 10:45
  • I mean that while `@ContributesFragmentInjector` might handle the `@BindsInstance` of the fragment internally, you should not do `return new SomeFragment()` manually from some provider method. – EpicPandaForce Oct 07 '17 at 11:46

5 Answers5

51

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

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
34

In kotlin, you can leverage companion object

@Module
interface MyDaggerModule {

    @Binds
    fun provideSomething(somethingImpl: SomethingImpl): Something

    companion object {

        @Provides
        fun provideAnotherThing(): AnotherThing {
            return AnotherThing()
        }
    }
}
Levon Petrosyan
  • 8,815
  • 8
  • 54
  • 65
31

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);
    }
}
gordinmitya
  • 967
  • 10
  • 10
  • 3
    Besides removing the abstract keyword boilerplate, is there an advantage/disadvantage to using an interface instead of an abstract class? – Paul T. Jun 06 '19 at 09:05
  • Besides removing the abstract keyword boilerplate, is there an advantage/disadvantage to using an interface instead of an abstract class? – Paul T. Jun 06 '19 at 09:07
0

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.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
canerkaseler
  • 6,204
  • 45
  • 38
0

Now You can do it like this to have the Provides and Binds at the same place:

@Module
@InstallIn(ActivityRetainedComponent::class)
internal abstract class YourModule {

  @ActivityRetainedScoped
  @Binds
  abstract fun bindRepository(
    Repository: RepositoryImpl
  ) : Repository

  companion object {
    @Provides
    fun provideApi(
      params: Param
    ) : Api {
      val retrofit = interactorFactory
        .newInteractor<Unit, Unit>()
        .interaction()
        .buildRetrofitClient()
      return retrofit.create(Api::class)
    }
  }

}
Arash Afsharpour
  • 1,282
  • 11
  • 22