SqueezyMo
SqueezyMo

Reputation: 1726

Dagger 2 error: dependency “cannot be provided without an @Inject constructor”

I'm trying to use Dagger 2 in my Android project. For starters, I want to make use of two components responsible for injecting application-wide and activity-wide dependencies respectively. As a basic reference, among other things I used this answer.

So there are two different ways of setting relationship between components: with the @Subcomponent annotation and with the dependencies parameter.

  1. If I go with the first one, my AppContextComponent works fine. But as soon as I try to inject dependency from the ActivityContextComponent, I get this build-time error:

Error:com.example.ui.activity.MainActivity cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.

  1. With the second approach, I get this error for all dependencies.

Knowing that I do have provision methods, this error message is frankly of no use. Here is a couple of other possible sources of this error I've managed to google out but they don't seem to be the case:

  1. Scoping problems.
  2. Ambuiguity due to the same return type from different provision methods (the problem persists even if I remove methods providing Context).

The following code is in Kotlin but I reckon it should be pretty straightforward.

Application component

object AppContext {
    // a singleton used in place of a Java static field
    @JvmStatic lateinit var graph: AppContextComponent
}

@ApplicationScope @Component(modules = arrayOf(AppContextModule::class))
interface AppContextComponent {
    fun inject(fragment: BaseFragment)

    fun newActivityContextComponent(module: ActivityContextModule): ActivityContextComponent
}

@Module
class AppContextModule(val app: MyApplication) {
    @Provides @ApplicationScope @ForApplication
    fun provideContext(): Context {
        return app;
    }

    @Provides @ApplicationScope
    fun provideMyApplicationContext(): MyApplication {
        return app;
    }
}

Activity component

object ActivityContext {
    @JvmStatic lateinit var graph: ActivityContextComponent
}

@ActivityScope @Subcomponent(modules = arrayOf(ActivityContextModule::class))
interface ActivityContextComponent {
    fun inject(fragment: BaseFragment)
}

@Module
class ActivityContextModule(val activity: MainActivity) {

    // Worth pointing out is that the provision methods are not used. 
    // That is, no code is being generated for them.

    @Provides @ActivityScope @ForActivity
    fun provideContext(): Context {
        return activity;
    }

    @Provides @ActivityScope
    fun provideMainActivityContext(): MainActivity {
        return activity;
    }
}

And below is how these components are used.

Application

class MyApplication : SugarApp() {
    override fun onCreate() {
        super.onCreate()
        AppContext.graph =
            DaggerAppContextComponent
                    .builder()
                    .appContextModule(AppContextModule(this))
                    .build()
    }
}

Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityContext.graph = 
            AppContext.graph
                 .newActivityContextComponent(ActivityContextModule(this))
    }
}

BaseFragment (where the actual injection happens)

abstract class BaseFragment : Fragment() {
    // works fine
    @Inject @ForApplication lateinit var myApplication: MyApplication

    // fails with "cannot be provided" error
    @Inject @ForActivity lateinit var myActivity: MainActivity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityContext.graph.inject(this)
    }
}

What am I missing?

Thanks!

Upvotes: 3

Views: 5330

Answers (1)

AndroidEx
AndroidEx

Reputation: 15824

EDIT

The gist is:

If you use dagger's @Subcomponents, make sure you don't have a method in the parent @Component that injects into the same target class as your subcomponent does.

Dagger will get confused and will try to generate code to inject into the target directly from the parent component, while it doesn't have the needed module(s) to satisfy the injected dependency.

Upvotes: 12

Related Questions