Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subcomponent + BindsInstance doesn't work #212

Open
Dkhusainov opened this issue Dec 8, 2020 · 1 comment
Open

Subcomponent + BindsInstance doesn't work #212

Dkhusainov opened this issue Dec 8, 2020 · 1 comment

Comments

@Dkhusainov
Copy link

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.

@Dkhusainov
Copy link
Author

After further investigation it looks like a duplicate of #208

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant