47

I'm configuring the new Dagger Android module but I got this error Here's my Component:

@AppScope
@Component(modules = {AppModule.class, NetModule.class})
public interface AppComponent {

  @Component.Builder
  interface Builder {
    @BindsInstance
    Builder application(ExampleApplication application);

    @BindsInstance
    Builder appModule(AppModule appModule);

    @BindsInstance
    Builder netModule(NetModule netModule);

    AppComponent build();
  }

  void inject(ExampleApplication __); 
...

Which I build like this in my Application

appComponent = DaggerAppComponent
      .builder()
      .application(this)
      .appModule(new AppModule(this))
      .netModule(new NetModule())
      .build()
      .inject(this);

But I still receive the error

Error:(20, 3) error: @Component.Builder is missing setters for required modules or components: [app.example.com.dagger.AppModule]

According to the documentation that should be right, What am I missing?

For example, this could be a valid Component with a Builder:

@Component(modules = {BackendModule.class, FrontendModule.class})
interface MyComponent {
  MyWidget myWidget();

  @Component.Builder
  interface Builder {
    MyComponent build();
    Builder backendModule(BackendModule bm);
    Builder frontendModule(FrontendModule fm);
  }
}
crgarridos
  • 8,758
  • 3
  • 49
  • 61
Leonardo Deleon
  • 2,577
  • 5
  • 15
  • 22
  • 2
    Did you try without the `@BindsInstance` on your module methods? – David Medenjak May 20 '17 at 08:36
  • @DavidMedenjak I'm following this [tutorial](https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3) Which Module should have that? The App Module? – Leonardo Deleon May 20 '17 at 08:46
  • 2
    If you have a look at the code you provided above, one is your own, the other is part of the dagger documentation. The visible difference is, that _you_ have a `@BindsInstance` annotation on your `AppModule` and `NetModule` builder methods. – David Medenjak May 20 '17 at 08:48
  • 1
    Me also got same issue – Saravanan May 31 '17 at 11:03

5 Answers5

56

Remove the below code from the AppModule.class and rebuild the project

    @Provides
    @Singleton
    Application provideContext(SomeApplication application) {
        return application;
    }
Gnanendra Kumar
  • 1,096
  • 11
  • 11
  • 11
    Thanks for the answer, would be curious to know why this works? – nmu Jul 03 '17 at 10:49
  • 9
    Component.Builder is a custom builder for the AppComponent. You provide BindsInstance for application. That's how the AppModule knows to get application without Provides Singleton. You use the custom builder so you don't need to pass objects via the AppModule constructor. – LEO Sep 08 '17 at 17:42
  • thats super-answer & @LEO superb expalanation thanks , but how now we can get application-instance. – Tushar Pandey Dec 25 '17 at 10:46
  • Same way you get any provided object – LEO Jan 02 '18 at 22:48
  • 2
    I'm confused, the error says that we need to *add* a setter, but you actually removed a "provides". In my thinking that would not solve the problem, but introduce another problem that is cannot `@Inject` the Application because AppModule no longer `@Provides` Application . But you say this works, can you clarify your answer to explain how it works (non-intuitively)? Thank you. – Hendy Irawan Jan 13 '18 at 09:27
  • we use @.BindInstance when it came to we want to provide a dependency that we got in a runtime, for example above, The instance of application retrieved during runtime process and when using @.Builder, dependency that we received at runtime no need required to be annotated @.Provides inside the Module – mangkool Jan 18 '20 at 09:11
24

I think this provides a somewhat clearer explanation on the use of @BindsInstance and removal of @Provides Application, Dagger 2 Component Builder:

@BindsInstance What?

Here’s the definition :

Marks a method on a component builder or subcomponent builder that allows an instance to be bound to some type within the component. — source

WHAAT? I don’t understand it either

Here’s a simple hint of when to use it :

@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values. — source

I come from Spring Boot and Dagger 2 is OMG so much more complicated. :(

So based on my extremely limited experience with Dagger 2, this happens because there a *Module with a constructor argument which is improperly configured. I still don't know how to properly configure the Module with a constructor argument, but I rather follow recommended approach given by Dagger 2 documentation, and that is to remove the constructor argument(s) and use @BindsInstance and @Inject instead.

e.g.

@Module
class NetModule { // no constructor argument here!

    @Inject @Named("mqttServer") // replaced by @Inject
    internal lateinit var mqttServer: String

}

and in AppComponent :

@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, NetModule::class, ActivityBuilder::class])
interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        @BindsInstance // you'll call this when setting up Dagger
        fun mqttServer(@Named("mqttServer") mqttServer: String): Builder

        fun build(): AppComponent
    }

    fun inject(app: GeoAssistantApp)
}

Then you provide the dependencies of the modules when constructing the DaggerAppComponent from the Application subclass (make sure you specify the subclass name in AndroidManifest.xml):

class GeoAssistantApp : Application(), HasActivityInjector, HasSupportFragmentInjector {

    @Inject
    internal lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
    @Inject
    internal lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>

    override fun onCreate() {
        super.onCreate()
        Log.i(GeoAssistantApp::class.java.simpleName, "Initializing DaggerAppComponent...")
        DaggerAppComponent.builder()
                // list of modules/dependencies of modules that are part of this component need to be created here too
                .application(this)
                .mqttServer(getString(R.string.mqtt_server))
                .build()
                .inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity> {
        return activityDispatchingAndroidInjector
    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return fragmentDispatchingAndroidInjector
    }
}

Note that the support-v4 Fragment vs native Fragment usage can be a source of problems. e.g. for support-v4, you need to use AndroidSupportInjectionModule, HasSupportFragmentInjector, while with native, you need to use AndroidInjectionModule, HasFragmentInjector.

Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
18

In my case I was using an object Module, so I had to annotate provider method with @JvmStatic

@Module
object GsonModule {

    @JvmStatic
    @Singleton
    @Provides
    fun provideGson() = Gson()

}
Javad
  • 361
  • 5
  • 9
Federico Torres
  • 181
  • 1
  • 2
2

Kotlin Answer

Check your constructor of Module.

If you have constructor and application parameter, delete it.

TRUE usage:

@Module
class MyModule { 
   // This is example module for true usage.
}

••• Wrong usage:

@Module
class MyModule constructor(application: Application) { 
   // This is example module for wrong usage.
}
canerkaseler
  • 6,204
  • 45
  • 38
0

In my case my module class has a constructor and removing @BindInstance is the solution.

the module:

@Module
class SomeModule(private val count:Int)

the component builder:

@Component.Builder
interface Builder {
    fun engineModule(someModule: SomeModule): Builder
}
Talha Ç
  • 74
  • 1
  • 8