1
votes

I am using Dagger 2.14.1 in my Android application and migrated recently from 2.10. The new version makes it much easier to inject Activities and Fragments, but I can't find a way to inject a custom class where I cannot change the constructor as well. With 2.10 I could write custom inject functions and then use them to simply inject any class:

Simplified Dagger 2.10 Injection with Kotlin:

@Singleton
@Component(dependencies = [], modules = [ApplicationModule::class, ...])
interface ApplicationComponent {

    fun application(): App

    fun inject(authenticationActivity: AuthenticationActivity)

    fun inject(converters: Converters)

    // ...
}

class App : Application() {

    companion object {
        @JvmStatic
        lateinit var component: ApplicationComponent
    }

    override fun onCreate() {
        super.onCreate()
        component = DaggerApplicationComponent.builder()
            .applicationModule(ApplicationModule(this))
            .build()
    }
}
// This class is used by the Room database framwork and I cannot change the constructor and do class Converters @Inject constructor(private val gson: Gson) {
class Converters {

    @Inject
    protected lateinit var gson: Gson

    init {
        App.component.inject(this)
    }

    // ...
}

Simplified Dagger 2.14.1 Injection with Kotlin doesn't provide me with a ApplicationComponent Object to inject my custom classess:

@Singleton
@Component(modules = [ApplicationModule::class, ...])
interface ApplicationComponent : AndroidInjector<App> {

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<App>()

    fun inject(converters: Converters) // doesn't work!
}

class App : Application(), HasActivityInjector {

    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()
        // From where do I get my component to call .inject() from another class?
        DaggerApplicationComponent.builder().create(this).inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}

In short: the DaggerApplicationComponent.builder().create(this) returns an AndroidInjector<App!> Object. This Object only has one .inject() functions which only accepts my App Class. So I cannot inject anything else.

Of course there is a lot missing, but I wanted to only post the relevant code. My Dagger 2.14.1 implementation works and all my dependencies in Activities, Fragments and View Models get injected.

I am quite new to Dagger and the more I use it the more I love it, but I couldn't figure out on how to inject custom classes where I cannot change the constructor. Thanks for you help!

1
Why don't you store the component in App just like before? - EpicPandaForce
I extended my question with: In short: the DaggerApplicationComponent.builder().create(this) returns an AndroidInjector<App!> Object. This Object only has one .inject() functions which only accepts my App Class. So I cannot inject anything else. I simply cannot inject anything else than my App. - Paul Spiesberger
A-ha. I think you can do @Inject lateinit var component: AppComponent in App class and it should work. - EpicPandaForce
Ha! Great, It worked and I can now inject my Converters class locally in my Application class. But, there is one problem, when I make my @Inject lateinit var component: ApplicationComponent static by putting it into the companion object I get a Dagger Exception: error: Dagger does not support injection into static fields. Any idea on how to make it accessible to other custom classes to inject themselves in their init {} block? - Paul Spiesberger
I'd just add a val component: AppComponent get() = appInstance.component and an appInstance field to companion object and be on my merry way (although I admit it'd be nicer if you could have package visibility for the instance-level component, you might want to inject it into something else). Hmm... - EpicPandaForce

1 Answers

2
votes

Try this:

class App : Application(), HasActivityInjector {

    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    companion object {
        @JvmStatic
        lateinit var instance: App
    }

    @Inject
    lateinit var component: ApplicationComponent 

    override fun onCreate() {
        super.onCreate()
        instance = this

        DaggerApplicationComponent.builder().create(this).inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}

Then

object Injector {
    @JvmStatic fun get(): ApplicationComponent = App.instance.component
}

Now you can do

class Converters {

    @Inject
    protected lateinit var gson: Gson

    init {
        Injector.get().inject(this)
    }