13

I decided to learn dagger dependency injection framework. After some tutorials I try to implement dagger into my project. However I got this error

com\assigment\di\component\AppComponent.java:11: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> cannot be provided without an @Provides-annotated method.
    public abstract void inject(@org.jetbrains.annotations.NotNull()
                         ^
      java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> is injected at
          dagger.android.DispatchingAndroidInjector.<init>(injectorFactories)
      dagger.android.DispatchingAndroidInjector<android.app.Activity> is injected at
          assigment.com.assigment.App.activityInjector
      assigment.com.assigment.App is injected at
          assigment.com.assigment.di.component.AppComponent.inject(assigment.com.assigment.App)

I try to solve this for 2 days but with no luck. So here's how I set up my project

class App : Application(), HasActivityInjector  {
    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    lateinit var appComponent: AppComponent

    override fun onCreate() {
        super.onCreate()

        appComponent = DaggerAppComponent
                .builder()
                .appModule(AppModule())
                .build()
    }

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

Here's my app component

@Singleton
@Component(modules = [AppModule::class])

interface AppComponent : AndroidInjector<App> {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): AppComponent
    }
    override fun inject(app: App)
}

and here's my appModule class

AppModule

@Module
class AppModule {
    private val url = "http://test.lt/v1/"

    @Provides
    @Singleton
    fun provideApplication(app: Application): Context = app
}

So what I'm missing with this implementation?

David
  • 3,055
  • 4
  • 30
  • 73

3 Answers3

23

Add AndroidInjectionModule.class and ActivityBuilder.class to your AppComponent

@Singleton
@Component(modules = [AndroidInjectionModule::class, AppModule::class, ActivityBuilder::class ])

interface AppComponent : AndroidInjector<App> {

  @Component.Builder
  interface Builder {
    @BindsInstance
    fun application(application: Application): Builder
    fun build(): AppComponent
  }
 override fun inject(app: App)
}
eurosecom
  • 2,932
  • 4
  • 24
  • 38
7

You have two problems.

To get rid of the compiler error add AndroidInjectionModule::class to Component modules:

@Singleton
@Component(modules = [AndroidInjectionModule::class, AppModule::class])
interface AppComponent : AndroidInjector<App> {


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

    override fun inject(app: App)
}

With @Component.Builder annotated interface you define the builder interface with an annotated @BindsInstance method application() (note that there is not an appModule method declared in the Builder).

With such declaration you can build your component using application(this):

    appComponent = DaggerAppComponent
            .builder()
            .application(this)
            .build()

In this way this application instance is bound inside the component.

Just as a side note: Binding Instances are documented here, but personally I found the explanation quite hard to grasp for someone learning dagger, like me.

attdona
  • 17,196
  • 7
  • 49
  • 60
3

Detail answer with explanation

Component - > Component is a graph. Component will provide injected instances by using modules.

@Component(
    modules = [
        AndroidInjectionModule::class, //We didn’t create this. It is an internal class in Dagger 2.10. Provides our activities and fragments with given module
        ActivityModule::class,
    ]
)
@Singleton
interface AppComponent : AndroidInjector<App> {

    @Component.Builder
    interface Builder {
        fun addContext(@BindsInstance context: Context): Builder
        fun build(): AppComponent
    }
}

We created ActivityModule module. This is a given module to dagger. We map all our activities here. And Dagger know our activities in compile time. In our app we have MainActivity. So we map it here.

@Module
abstract class ActivityModule {

    @ContributesAndroidInjector
    public abstract MainActivity bindMainActivity();
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application
        android:name=".App"

App.kt

class App : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().addContext(this).build()
    }
}

And Make sure to extend your activity with DaggerAppCompatActivity that will auto inject before onCreate

Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300