Reputation: 1726
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.
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.
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:
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
Reputation: 15824
EDIT
The gist is:
If you use dagger's @Subcomponent
s, 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