Best practices for implementing dagger in multi module projects

The problem

My solution

App Module//Dagger
api "com.google.dagger:dagger:$rootProject.dagger"
kapt "com.google.dagger:dagger-compiler:$rootProject.dagger"
api "com.google.dagger:dagger-android:$rootProject.dagger"
kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger"
//Modules
implementation project(path: ':core')
implementation project(path: ':login')
implementation project(path: ':utilities')
Core Module//Dagger
api "com.google.dagger:dagger:$rootProject.dagger"
kapt "com.google.dagger:dagger-compiler:$rootProject.dagger"
api "com.google.dagger:dagger-android:$rootProject.dagger"
kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger"
Login module//Dagger
api "com.google.dagger:dagger:$rootProject.dagger"
kapt "com.google.dagger:dagger-compiler:$rootProject.dagger"
api "com.google.dagger:dagger-android:$rootProject.dagger"
kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger"
//Modules
implementation project(path: ':core')
Utilities Module//Dagger
api "com.google.dagger:dagger:$rootProject.dagger"
kapt "com.google.dagger:dagger-compiler:$rootProject.dagger"
api "com.google.dagger:dagger-android:$rootProject.dagger"
kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger"
//Modules
implementation project(path: ':core')
@Component(
modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class
],
dependencies = [CoreComponent::class, UtilsComponent::class]
)
@AppScope
interface AppComponent : AndroidInjector<MainApplication> {

@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): AppComponent.Builder

fun coreComponent(coreComponent: CoreComponent): AppComponent.Builder
fun utilsComponent(utilsComponent: UtilsComponent): AppComponent.Builder
fun build(): AppComponent
}
}
@Module
abstract class ActivityBindingModule {

@ContributesAndroidInjector
abstract fun mainActivity(): MainActivity

@ContributesAndroidInjector(modules = [LoginModule::class])
abstract fun loginActivity(): LoginActivity
}
@Module
class LoginModule {
@Provides
fun provideFoo() = Foo()
}
@Component
@Singleton
interface CoreComponent {
fun getUserController(): UserController

}
@Component(modules = [UtilsModule::class])
interface UtilsComponent {

fun getResourceProvider() : IResourceProvider

@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): UtilsComponent.Builder

fun build(): UtilsComponent
}
}
@Module
abstract class UtilsModule {

@Binds
abstract fun bindContext(application: Application): Context

@Module
companion object {
@Provides
@JvmStatic
fun bindResourceProvider(context: Context): IResourceProvider {
return ResourceProviderImpl(context)
}
}

}
class MainApplication : DaggerApplication() {


fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.application(this)
.coreComponent(provideCoreComponent())
.utilsComponent(getUtilsComponent())
.build()

}


fun provideCoreComponent(): CoreComponent {
return DaggerCoreComponent
.builder()
.build()
}

private fun getUtilsComponent(): UtilsComponent {
return DaggerUtilsComponent
.builder()
.application(this)
.build()
}


}

Why Core Module?

class ResourceProviderImpl(val context: Context) : IResourceProvider {

override fun getString(resourceId: Int): String {
return context.getString(resourceId).orEmpty()
}

override fun getString(id: Int, vararg args: Any?): String {
return context.getString(id, *args)
}


override fun getDrawable(drawableRes: Int): Drawable {
return requireNotNull(ContextCompat.getDrawable(context, drawableRes))
}
}

Injecting dependencies

class LoginActivity : AppCompatActivity() {

@Inject
lateinit var foo: Foo()

@Inject
lateinit var userController: UserController

@Inject
lateinit var resourceProvider: IResourceProvider

override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)


val textView = findViewById<TextView>(R.id.textView)
textView.text = resourceProvider.getString(R.string.app_name)
}
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store