0

I am stuck here with dependent dagger component throwing an error android.content.Context cannot be provided without an @Provides-annotated method.

Here is my CoreDataComponent, which generates code for coreDataModule and contextModule

@Singleton
@Component( modules = [
        CoreDataModule::class,
        ContextModule::class
    ]
)
interface CoreDataComponent{

    @Component.Builder interface Builder {
        fun build(): CoreDataComponent

        fun coreDataModule(coreDataModule: CoreDataModule) : Builder
        fun contextModule(contextModule: ContextModule) : Builder
    }
}

CoreDataModule

@Module
class CoreDataModule {
    @Provides
    fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        return HttpLoggingInterceptor().apply {
            level = if (BuildConfig.DEBUG) {
                HttpLoggingInterceptor.Level.BODY
            } else {
                HttpLoggingInterceptor.Level.NONE
            }
        }
    }

    @Provides
    fun provideOkHttpClient(context: Context): OkHttpClient =
        OkHttpClient.Builder()
            .addInterceptor(provideLoggingInterceptor())
            .addInterceptor(ChuckInterceptor(context))
            .addInterceptor { chain ->
                val url = chain.request().url().newBuilder().addQueryParameter("access_token", "").build()
                val request = chain.request().newBuilder().url(url).build()

                chain.proceed(request)
            }
            .cache(null)
            .build()

    @Provides
    fun provideRetrofit(
        okHttpClient: OkHttpClient
    ): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_DOMAIN)
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .client(okHttpClient)
            .build()
    }

}

ContextModule

@Module
class ContextModule(val context: Context) {

    @Provides
    fun provideApplicationContext(): Context {
        return context
    }
}

Here is my dependant component

@Component(
    modules = [
        AuthDataModule::class,
        AuthViewModelModule::class
    ],
    dependencies = [
        CoreDataComponent::class
    ]
)
@ActivityScope
interface AuthDataComponent {
    @Component.Builder interface Builder {
        fun build() : AuthDataComponent

        fun coreDataComponent(coreDataComponent: CoreDataComponent) : Builder
        /*@BindsInstance
        fun activityContext(context: Context) : Builder*/
    }

    fun inject(authenticationActivity: AuthenticationActivity)
    fun inject(loginFragment: LoginFragment)
    ....
}

Here is how I build the CoreDataComponent and AuthDataComponent

class Application : Application() {

    private val coreDataComponent: CoreDataComponent by lazy {
        DaggerCoreDataComponent
            .builder()
            .coreDataModule(CoreDataModule())
            .contextModule(ContextModule(this))
            .build()
    }

    companion object {
        @JvmStatic
        fun coreDataComponent(context: Context) =
            (context.applicationContext as Application).coreDataComponent
    }

}

/// Injection
fun AuthenticationActivity.inject() {
    DaggerAuthDataComponent.builder()
        .coreDataComponent(Application.coreDataComponent(this))
        //.activityContext(this)
        .build()
        .inject(this)

Error Log

error: android.content.Context cannot be provided without an @Provides-annotated method.
    public abstract void inject(@org.jetbrains.annotations.NotNull()

android.content.Context is injected at
          com.stylabs.collabb.core.dagger.module.CoreDataModule.provideOkHttpClient(context)   

I don't understand this, parent component has already provided the context.

Update 1 This is only happening if I inject AuthDataComponent Dependancy to Fragment, not throwing error in Activity

Update 2 Here is full error trace

 public abstract void inject(@org.jetbrains.annotations.NotNull()
                         ^
      android.content.Context is injected at
          com.stylabs.collabb.core.dagger.module.CoreDataModule.provideOkHttpClient(context)
      okhttp3.OkHttpClient is injected at
          com.stylabs.collabb.core.dagger.module.CoreDataModule.provideRetrofit(okHttpClient)
      retrofit2.Retrofit is injected at
          com.stylabs.collabb.features.auth.data.dagger.module.AuthDataModule.providesAuthApi(retrofit)
      com.stylabs.collabb.features.auth.data.source.AuthApi is injected at
          com.stylabs.collabb.features.auth.data.dagger.module.AuthDataModule.providesAuthRemoteDataSource(authApi)
      com.stylabs.collabb.features.auth.data.source.AuthRemoteDataSource is injected at
          com.stylabs.collabb.features.auth.data.dagger.module.AuthDataModule.providesAuthRepository(authRemoteDataSource)
      com.stylabs.collabb.features.auth.data.source.AuthRepository is injected at
          com.stylabs.collabb.features.auth.data.AuthViewModel.<init>(authRepository)
      com.stylabs.collabb.features.auth.data.AuthViewModel is injected at
          com.stylabs.collabb.features.auth.login.ui.LoginFragment.authViewModel
      com.stylabs.collabb.features.auth.login.ui.LoginFragment is injected at
          com.stylabs.collabb.features.auth.data.dagger.component.AuthDataComponent.inject(loginFragment)
Suyash Chavan
  • 776
  • 9
  • 20
  • 1
    Please include the full error trace (`Cannot be provided...`) with the call stack. At a glance it seems you're missing a provision method in your parent component(s). – David Medenjak May 01 '19 at 07:44
  • @DavidMedenjak I have added the error trace – Suyash Chavan May 01 '19 at 08:36
  • @DavidMedenjak Sorry it may sound silly, but I need to know if I am doing something wrong here plus I am new to dagger. Is there any other way to achieve this? – Suyash Chavan May 01 '19 at 12:07
  • 1
    If you take a close [look at the trace](https://stackoverflow.com/a/44912081/1837367) it happens within your AuthDataComponent when you try to inject a fragment which ultimately needs a `OkHttpClient` whose @Provides method needs the `Context`. For some reason you commented out the context binding on your builder (?), with it it should work. I don't know why your component injects both the activity and fragment (that's not the usual setup—one component per framework class) but I guess your activity doesn't use the OkHttpClient, that's why you can inject the activity, but not the fragment – David Medenjak May 04 '19 at 08:37
  • I understood the OkHttpClient requires context which is what the error is indicating, but how can I expose the singleton instance of retrofit to its dependant component and leave its initialization to Application. The Injection to fragments is due to the fact that I am following single activity multiple fragments patterns so I am passing the same viewmodel instance to all the fragments of Authentication activity. – Suyash Chavan May 04 '19 at 16:06
  • And I should have read dependant component more throughly, Now I know why it was not working I was using builder inside my coreDataComponent. I suspect it is wrong and I only need to expose the dependancy which will be used by dependant component which is Retrofit. So I have done that and it works. The dependant components are using singleton instance of retrofit. – Suyash Chavan May 04 '19 at 16:46

0 Answers0