Hi!
I trying to use multibinding to provide OkHttp Interceptors for the client.
@Provides
@Singleton
fun provideOkHttpClient(interceptors: Set<Interceptor>): OkHttpClient {
val builder = OkHttpClient.Builder()
for (interceptor in interceptors) {
builder.addNetworkInterceptor(interceptor)
}
return builder.build()
}
@Provides @IntoSet
@Singleton
fun provideLoggingInterceptor(): Interceptor =
HttpLoggingInterceptor({ message -> Timber.tag("OkHttp").d(message) })
but can't compile. There is an error:
error: java.util.Set<? extends okhttp3.Interceptor> cannot be provided without an @Provides-annotated method.
When I provide strings in the same way all works. But not with the Interceptor.
What I use:
Kotlin 1.1.1 (kapt{generateStubs = true}) ,
Dagger 2.10
Here is full stacktrace if it can help:
error: java.util.Set<? extends okhttp3.Interceptor> cannot be provided without an @Provides-annotated method.
java.util.Set<? extends okhttp3.Interceptor> is injected at
ru.mobileup.igooods.network.NetworkModule.provideOkHttpClient(arg0)
okhttp3.OkHttpClient is injected at
ru.mobileup.igooods.network.NetworkModule.provideRetrofit(arg0, …)
retrofit2.Retrofit is injected at
ru.mobileup.igooods.network.NetworkModule.provideApi(arg0, …)
ru.mobileup.igooods.network.IgooodsApi is injected at
ru.mobileup.igooods.gateway.GatewayModule.provideAuthGateway(arg0, …)
ru.mobileup.igooods.gateway.AuthGateway is injected at
ru.mobileup.igooods.ui.main.MainPresenter.authGateway
ru.mobileup.igooods.ui.main.MainPresenter is injected at
ru.mobileup.igooods.AppComponent.inject(arg0)
1 error
:app:compileStubDebugJavaWithJavac FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileStubDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
* Try:
Run with --info or --debug option to get more log output.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileStubDebugJavaWithJavac'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:236)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:228)
at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:61)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:228)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:215)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:77)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:58)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:32)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:113)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:196)
at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:193)
at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:193)
at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:119)
at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:102)
at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:71)
at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:44)
at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:29)
at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:47)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
at org.gradle.util.Swapper.swap(Swapper.java:38)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see the compiler error output for details.
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:48)
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:33)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:104)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:53)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:38)
at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:35)
at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:25)
at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilationFinalizer.execute(IncrementalCompilationFinalizer.java:39)
at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilationFinalizer.execute(IncrementalCompilationFinalizer.java:24)
at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:206)
at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:164)
at com.android.build.gradle.tasks.factory.AndroidJavaCompile.compile(AndroidJavaCompile.java:49)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:163)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
... 70 more
Dagger expects a java.util.Set<okhttp3.Interceptor>, not ? extends okhttp3.Interceptor. The problem is that you're using Kotlin and Kotlin will by default translate a Set<X> into a Set<? extends X>: http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics
Apparently (according to the docs), you should be able to make it work with Set<@JvmSuppressWildcards okhttp3.Interceptor>.
Wrt Set<String>, it works because String is final, so Kotlin emits a Set<String> as Set<? extends String> would have no sense. See this note in the docs:
NOTE: when the argument type is final, there's usually no point in generating the wildcard, so
Box<String>is alwaysBox<String>, no matter what position it takes.
Thanks for the great explanation!
Now all works.
This keeps coming up. I wonder if we should try to inspect the classpath for kotlin classes (if that's possible?) and present an error when we see this
This keeps coming up. I wonder if we should try to inspect the classpath for kotlin classes (if that's possible?) and present an error when we see this
I contributed this code to Spring that will give you the detection bit:
It might also be worth adding a few kotlin tips and tricks to the dagger docs, since I think there are a small number of super-common interop mis-matches with easy workarounds. Now that I've dug into this issue, having hit it in my own efforts, I see the answer in six places.
Like, @tbroyer's comment above is basically nearly the perfect FAQ answer. :)
We could have a dagger-kotlin annotation processor that recommended that people use @JvmSuppressWildcards at injection sites. That might be annoying if someone did actually want to inject a wildcard-extending set though.
If something cannot be provided, and it has wildcard type arguments, and it could be provided without wildcards (could be complex depending on number of type arguments), and there's kotlin in the compile classpath, then suggest to add the annotation. WDYT?
I wish the link to this issue was added to the error message 🗡
@tbroyer I am still facing this isssue.
[Dagger/MissingBinding] @com.coders.currencytracker.di.qualifiers.AppInterceptor java.util.Set
@Provides
@NetworkInterceptor
@IntoSet
@Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor { message -> Log.d("OkHttp", message) }
.setLevel(HttpLoggingInterceptor.Level.BODY)
}
@Provides
@NetworkInterceptor
@IntoSet
@Singleton
fun provideStethoInterceptor(): StethoInterceptor {
return StethoInterceptor()
}
@Provides
@AppInterceptor
@IntoSet
@Singleton
fun provideChuckInterceptor(@ForApplication context: Context): ChuckInterceptor {
return ChuckInterceptor(context)
}
@Provides
@Singleton
fun provideApiOkHttpClient(
@AppInterceptor appInterceptor: Set<@JvmSuppressWildcards Interceptor>,
@NetworkInterceptor networkInterceptor: Set<@JvmSuppressWildcards Interceptor>
): OkHttpClient {
val okBuilder = OkHttpClient.Builder()
okBuilder.interceptors().addAll(appInterceptor)
okBuilder.networkInterceptors().addAll(networkInterceptor)
return okBuilder.build()
}
You made one set of HttpLoggingInterceptor, another of StethoInterceptor and a third of ChuckInterceptor (with qualifiers). Change your return types to Interceptor.
Most helpful comment
Dagger expects a
java.util.Set<okhttp3.Interceptor>, not? extends okhttp3.Interceptor. The problem is that you're using Kotlin and Kotlin will by default translate aSet<X>into aSet<? extends X>: http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-genericsApparently (according to the docs), you should be able to make it work with
Set<@JvmSuppressWildcards okhttp3.Interceptor>.Wrt
Set<String>, it works becauseStringisfinal, so Kotlin emits aSet<String>asSet<? extends String>would have no sense. See this note in the docs: