Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject only certain params in constructor

I have the presenter

class PhonePresenter @Inject constructor(
    private val preference: DataPreference,
    private val ioScheduler: Scheduler = Schedulers.io())

ioScheduler is a default parameter. I want to inject only preference: DataPreference. Now I have the exception

[dagger.android.AndroidInjector.inject(T)] io.reactivex.Scheduler cannot be provided without an @Provides-annotated method.

Is there any way to define parameters which I want to inject in a constructor?

like image 933
vmtrue Avatar asked Dec 24 '17 14:12

vmtrue


2 Answers

Make inject constructor with secondary constructor

class PhonePresenter(
    private val preference: DataPreference,
    private val ioScheduler: Scheduler) {        
    @Inject constructor(preference: DataPreference) : this(preference, Schedulers.io())
}
like image 156
luffy Avatar answered Nov 15 '22 18:11

luffy


Dagger is responsible for injection, let it do it's job. Don't use default parameters (Dagger doesn't care), this will force you to make concious decisions about your dependencies.

Two approaches come to mind:

1. Use Dagger to inject

Create a qualifier so Dagger can diferentiate between types of schedulers you might want to inject and a module that provides default IO scheduler.

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@MustBeDocumented
annotation class ForIo

@Module
class SchedulerModule {
     @Provides @ForIo
     fun provideIoScheduler() : Scheduler = Schedulers.io()
}

class PhonePresenter @Inject constructor(
    private val preference: DataPreference,
    @ForIo private val ioScheduler: Scheduler
) { ... }

Add SchedulerModule to your component as usual.

The correct way to supply different arguments is to use a different component with different modules specialized e.g. for testing. Or when testing you'll call the constructor manually.

2. Avoid Dagger in this case

Alternatively you can remove the IO scheduler from constructor parameters. The names suggests it's never going to be anything else than Schedulers.io() so it makes little sense to make it parameterized; make it an implementation detail instead.

class PhonePresenter @Inject constructor(private val preference: DataPreference) { 
    private val ioScheduler = Schedulers.io()

    ... 
}
like image 38
Eugen Pechanec Avatar answered Nov 15 '22 18:11

Eugen Pechanec