dagger-reflect
dagger-reflect copied to clipboard
Subcomponent + BindsInstance doesn't work
I'm trying to use the library in an application that uses dagger to create Worker
's for WorkManager
(you can find many articles about it ).
Here's the code and a test case that mimics worker injection:
package foo
import dagger.*
import dagger.multibindings.IntoMap
import org.junit.Test
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Scope
import javax.inject.Singleton
import kotlin.reflect.KClass
import kotlin.test.assertEquals
//root component
@Component(
modules = [
WorkerFactoryModule::class,
FeatureModule::class
]
)
@Singleton
interface RootComponent {
val daggerWorkerFactory: DaggerWorkerFactory
}
//worker extension point implementation
abstract class ListenableWorker(val workerParameters: String)
@Target(AnnotationTarget.FUNCTION)
@Retention
@MapKey
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerScope
@Module(subcomponents = [WorkerFactoryComponent::class])
abstract class WorkerFactoryModule
@Subcomponent
//@WorkerScope
interface WorkerFactoryComponent {
fun workerFactories(): Map<Class<out ListenableWorker>, Provider<ListenableWorker>>
@Subcomponent.Factory
interface Factory {
fun create(@BindsInstance workerParameters: String): WorkerFactoryComponent
}
}
//worker factory
@Singleton
class DaggerWorkerFactory @Inject constructor(
private val factory: WorkerFactoryComponent.Factory
) {
fun createWorker(
workerClassName: String,
workerParameters: String
): ListenableWorker {
val workers = factory
.create(workerParameters)
.workerFactories()
val workerClass = Class
.forName(workerClassName)
.asSubclass(ListenableWorker::class.java)
val worker = workers.getValue(workerClass).get()
return worker
}
}
//feature worker implementation
@Module
abstract class FeatureModule {
@Binds @IntoMap @WorkerKey(FeatureWorker::class) abstract fun impl(w: FeatureWorker): ListenableWorker
}
//@WorkerScope
class FeatureWorker @Inject constructor(workerParameters: String) : ListenableWorker(workerParameters)
//test
class DaggerWorkerTest {
@Test
fun testWorkerInjection() {
val component = dagger.reflect.DaggerReflect.create(RootComponent::class.java)
// val component = DaggerRootComponent.create()
val daggerWorkerFactory = component.daggerWorkerFactory
val featureWorker = daggerWorkerFactory.createWorker(
workerClassName = FeatureWorker::class.java.name,
workerParameters = "foo"
)
assertEquals("foo", featureWorker.workerParameters)
}
}
Running the test gives the following error:
java.lang.IllegalStateException: Missing binding for java.lang.String
* Requested: foo.FeatureWorker
from @Inject[foo.FeatureWorker.<init>(…)]
* Requested: java.lang.String
which was not found.
If you uncomment 2 lines with //@WorkerScope
, you'll get a different(and incorrect) error about invalid scopes:
java.lang.IllegalStateException: Unable to find binding for key=foo.FeatureWorker with linker=Linker with Scope[@javax.inject.Singleton()]
Note that both cases work as expected with dagger codegen.
After further investigation it looks like a duplicate of #208