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.
- If I go with the first one, my
AppContextComponent
works fine. But as soon as I try to inject dependency from theActivityContextComponent
, 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.
- 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:
- Scoping problems.
- 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!