Dagger 2: @Component.Builder is missing setters for required modules or components: [appi.example.com.dagger.AppModule]`

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

    @Provides
    @Singleton
    Application provideContext(SomeApplication application) {
        return application;
    }

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.


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

}